<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, ResourceUpdate};
use crate::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams};
use crate::{BlobImageRequest, BlobImageDescriptor, BlobImageResources, TransactionMsg};
use crate::{FontKey, FontTemplate, FontInstanceData, FontInstanceKey, AddFont};
use crate::image_tiling::*;
use crate::units::*;
use crate::font::SharedFontInstanceMap;
use crate::euclid::{point2, size2};

pub const DEFAULT_TILE_SIZE: TileSize = 512;

use std::collections::HashMap;
use std::sync::Arc;

/// We use this to generate the async blob rendering requests.
struct BlobImageTemplate {
    descriptor: ImageDescriptor,
    tile_size: TileSize,
    dirty_rect: BlobDirtyRect,
    /// See ImageResource::visible_rect.
    visible_rect: DeviceIntRect,
    // If the active rect of the blob changes, this represents the
    // range of tiles that remain valid. This must be taken into
    // account in addition to the valid rect when submitting blob
    // rasterization requests.
    // `None` means the bounds have not changed (tiles are still valid).
    // `Some(TileRange::zero())` means all of the tiles are invalid.
    valid_tiles_after_bounds_change: Option<TileRange>,
}

struct FontResources {
    templates: HashMap<FontKey, FontTemplate>,
    instances: SharedFontInstanceMap,
}

pub struct ApiResources {
    blob_image_templates: HashMap<BlobImageKey, BlobImageTemplate>,
    pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
    fonts: FontResources,
}

impl BlobImageResources for FontResources {
    fn get_font_data(&self, key: FontKey) -> &FontTemplate {
        self.templates.get(&key).unwrap()
    }
    fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
        self.instances.get_font_instance_data(key)
    }
}

impl ApiResources {
    pub fn new(
        blob_image_handler: Option<Box<dyn BlobImageHandler>>,
        instances: SharedFontInstanceMap,
    ) -> Self {
        ApiResources {
            blob_image_templates: HashMap::new(),
            blob_image_handler,
            fonts: FontResources {
                templates: HashMap::new(),
                instances,
            }
        }
    }

    pub fn get_shared_font_instances(&self) -> SharedFontInstanceMap {
        self.fonts.instances.clone()
    }

    pub fn update(&mut self, transaction: &mut TransactionMsg) {
        let mut blobs_to_rasterize = Vec::new();
        for update in &transaction.resource_updates {
            match *update {
                ResourceUpdate::AddBlobImage(ref img) => {
                    self.blob_image_handler
                        .as_mut()
                        .unwrap()
                        .add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size);

                    self.blob_image_templates.insert(
                        img.key,
                        BlobImageTemplate {
                            descriptor: img.descriptor,
                            tile_size: img.tile_size,
                            dirty_rect: DirtyRect::All,
                            valid_tiles_after_bounds_change: None,
                            visible_rect: img.visible_rect,
                        },
                    );
                    blobs_to_rasterize.push(img.key);
                }
                ResourceUpdate::UpdateBlobImage(ref img) => {
                    debug_assert_eq!(img.visible_rect.size, img.descriptor.size);
                    self.update_blob_image(
                        img.key,
                        Some(&img.descriptor),
                        Some(&img.dirty_rect),
                        Some(Arc::clone(&img.data)),
                        &img.visible_rect,
                    );
                    blobs_to_rasterize.push(img.key);
                }
                ResourceUpdate::DeleteBlobImage(key) => {
                    self.blob_image_templates.remove(&key);
                }
                ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => {
                    self.update_blob_image(*key, None, None, None, area);
                    blobs_to_rasterize.push(*key);
                }
                ResourceUpdate::AddFont(ref font) => {
                    match font {
                        AddFont::Raw(key, bytes, index) => {
                            self.fonts.templates.insert(
                                *key,
                                FontTemplate::Raw(Arc::clone(bytes), *index),
                            );
                        }
                        AddFont::Native(key, native_font_handle) => {
                            self.fonts.templates.insert(
                                *key,
                                FontTemplate::Native(native_font_handle.clone()),
                            );
                        }
                    }
                }
                ResourceUpdate::AddFontInstance(ref instance) => {
                    // TODO(nical): Don't clone these.
                    self.fonts.instances.add_font_instance(
                        instance.key,
                        instance.font_key,
                        instance.glyph_size,
                        instance.options.clone(),
                        instance.platform_options.clone(),
                        instance.variations.clone(),
                    );
                }
                ResourceUpdate::DeleteFont(key) => {
                    self.fonts.templates.remove(&key);
                    if let Some(ref mut handler) = self.blob_image_handler {
                        handler.delete_font(key);
                    }
                }
                ResourceUpdate::DeleteFontInstance(key) => {
                    // We will delete from the shared font instance map in the resource cache
                    // after scene swap.

                    if let Some(ref mut r) = self.blob_image_handler {
                        r.delete_font_instance(key);
                    }
                }
                _ => {}
            }
        }

        let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize);
        transaction.blob_rasterizer = rasterizer;
        transaction.blob_requests = requests;
    }

    pub fn enable_multithreading(&mut self, enable: bool) {
        if let Some(ref mut handler) = self.blob_image_handler {
            handler.enable_multithreading(enable);
        }
    }

    fn update_blob_image(
        &mut self,
        key: BlobImageKey,
        descriptor: Option<&ImageDescriptor>,
        dirty_rect: Option<&BlobDirtyRect>,
        data: Option<Arc<BlobImageData>>,
        visible_rect: &DeviceIntRect,
    ) {
        if let Some(data) = data {
            let dirty_rect = dirty_rect.unwrap();
            self.blob_image_handler.as_mut().unwrap().update(key, data, visible_rect, dirty_rect);
        }

        let image = self.blob_image_templates
            .get_mut(&key)
            .expect("Attempt to update non-existent blob image");

        let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change(
            &image.visible_rect,
            visible_rect,
            image.tile_size,
        );

        match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) {
            (Some(old), Some(ref mut new)) => {
                *new = new.intersection(&old).unwrap_or_else(TileRange::zero);
            }
            (Some(old), None) => {
                valid_tiles_after_bounds_change = Some(old);
            }
            _ => {}
        }

        let blob_size = visible_rect.size;

        if let Some(descriptor) = descriptor {
            image.descriptor = *descriptor;
        } else {
            // make sure the descriptor size matches the visible rect.
            // This might not be necessary but let's stay on the safe side.
            image.descriptor.size = blob_size;
        }

        if let Some(dirty_rect) = dirty_rect {
            image.dirty_rect = image.dirty_rect.union(dirty_rect);
        }

        image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change;
        image.visible_rect = *visible_rect;
    }

    pub fn create_blob_scene_builder_requests(
        &mut self,
        keys: &[BlobImageKey]
    ) -> (Option<Box<dyn AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) {
        if self.blob_image_handler.is_none() || keys.is_empty() {
            return (None, Vec::new());
        }

        let mut blob_request_params = Vec::new();
        for key in keys {
            let template = self.blob_image_templates.get_mut(key).unwrap();

            // If we know that only a portion of the blob image is in the viewport,
            // only request these visible tiles since blob images can be huge.
            let tiles = compute_tile_range(
                &template.visible_rect,
                template.tile_size,
            );

            // Don't request tiles that weren't invalidated.
            let dirty_tiles = match template.dirty_rect {
                DirtyRect::Partial(dirty_rect) => {
                    compute_tile_range(
                        &dirty_rect.cast_unit(),
                        template.tile_size,
                    )
                }
                DirtyRect::All => tiles,
            };

            for_each_tile_in_range(&tiles, |tile| {
                let still_valid = template.valid_tiles_after_bounds_change
                    .map(|valid_tiles| valid_tiles.contains(tile))
                    .unwrap_or(true);

                if still_valid && !dirty_tiles.contains(tile) {
                    return;
                }

                let descriptor = BlobImageDescriptor {
                    rect: compute_tile_rect(
                        &template.visible_rect,
                        template.tile_size,
                        tile,
                    ).cast_unit(),
                    format: template.descriptor.format,
                };

                assert!(descriptor.rect.size.width > 0 && descriptor.rect.size.height > 0);
                blob_request_params.push(
                    BlobImageParams {
                        request: BlobImageRequest { key: *key, tile },
                        descriptor,
                        dirty_rect: DirtyRect::All,
                    }
                );
            });

            template.dirty_rect = DirtyRect::empty();
            template.valid_tiles_after_bounds_change = None;
        }

        let handler = self.blob_image_handler.as_mut().unwrap();
        handler.prepare_resources(&self.fonts, &blob_request_params);
        (Some(handler.create_blob_rasterizer()), blob_request_params)
    }
}

fn compute_valid_tiles_if_bounds_change(
    prev_rect: &DeviceIntRect,
    new_rect: &DeviceIntRect,
    tile_size: u16,
) -> Option<TileRange> {
    let intersection = match prev_rect.intersection(new_rect) {
        Some(rect) => rect,
        None => {
            return Some(TileRange::zero());
        }
    };

    let left = prev_rect.min_x() != new_rect.min_x();
    let right = prev_rect.max_x() != new_rect.max_x();
    let top = prev_rect.min_y() != new_rect.min_y();
    let bottom = prev_rect.max_y() != new_rect.max_y();

    if !left && !right && !top && !bottom {
        // Bounds have not changed.
        return None;
    }

    let tw = 1.0 / (tile_size as f32);
    let th = 1.0 / (tile_size as f32);

    let tiles = intersection
        .cast::<f32>()
        .scale(tw, th);

    let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
    let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
    let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
    let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };

    Some(TileRange {
        origin: point2(min_x as i32, min_y as i32),
        size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
    })
}
<commit_msg>Bug 1640564 - Delete the blob commands when we delete the blob image template. r=nical<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, ResourceUpdate};
use crate::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams};
use crate::{BlobImageRequest, BlobImageDescriptor, BlobImageResources, TransactionMsg};
use crate::{FontKey, FontTemplate, FontInstanceData, FontInstanceKey, AddFont};
use crate::image_tiling::*;
use crate::units::*;
use crate::font::SharedFontInstanceMap;
use crate::euclid::{point2, size2};

pub const DEFAULT_TILE_SIZE: TileSize = 512;

use std::collections::HashMap;
use std::sync::Arc;

/// We use this to generate the async blob rendering requests.
struct BlobImageTemplate {
    descriptor: ImageDescriptor,
    tile_size: TileSize,
    dirty_rect: BlobDirtyRect,
    /// See ImageResource::visible_rect.
    visible_rect: DeviceIntRect,
    // If the active rect of the blob changes, this represents the
    // range of tiles that remain valid. This must be taken into
    // account in addition to the valid rect when submitting blob
    // rasterization requests.
    // `None` means the bounds have not changed (tiles are still valid).
    // `Some(TileRange::zero())` means all of the tiles are invalid.
    valid_tiles_after_bounds_change: Option<TileRange>,
}

struct FontResources {
    templates: HashMap<FontKey, FontTemplate>,
    instances: SharedFontInstanceMap,
}

pub struct ApiResources {
    blob_image_templates: HashMap<BlobImageKey, BlobImageTemplate>,
    pub blob_image_handler: Option<Box<dyn BlobImageHandler>>,
    fonts: FontResources,
}

impl BlobImageResources for FontResources {
    fn get_font_data(&self, key: FontKey) -> &FontTemplate {
        self.templates.get(&key).unwrap()
    }
    fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
        self.instances.get_font_instance_data(key)
    }
}

impl ApiResources {
    pub fn new(
        blob_image_handler: Option<Box<dyn BlobImageHandler>>,
        instances: SharedFontInstanceMap,
    ) -> Self {
        ApiResources {
            blob_image_templates: HashMap::new(),
            blob_image_handler,
            fonts: FontResources {
                templates: HashMap::new(),
                instances,
            }
        }
    }

    pub fn get_shared_font_instances(&self) -> SharedFontInstanceMap {
        self.fonts.instances.clone()
    }

    pub fn update(&mut self, transaction: &mut TransactionMsg) {
        let mut blobs_to_rasterize = Vec::new();
        for update in &transaction.resource_updates {
            match *update {
                ResourceUpdate::AddBlobImage(ref img) => {
                    self.blob_image_handler
                        .as_mut()
                        .unwrap()
                        .add(img.key, Arc::clone(&img.data), &img.visible_rect, img.tile_size);

                    self.blob_image_templates.insert(
                        img.key,
                        BlobImageTemplate {
                            descriptor: img.descriptor,
                            tile_size: img.tile_size,
                            dirty_rect: DirtyRect::All,
                            valid_tiles_after_bounds_change: None,
                            visible_rect: img.visible_rect,
                        },
                    );
                    blobs_to_rasterize.push(img.key);
                }
                ResourceUpdate::UpdateBlobImage(ref img) => {
                    debug_assert_eq!(img.visible_rect.size, img.descriptor.size);
                    self.update_blob_image(
                        img.key,
                        Some(&img.descriptor),
                        Some(&img.dirty_rect),
                        Some(Arc::clone(&img.data)),
                        &img.visible_rect,
                    );
                    blobs_to_rasterize.push(img.key);
                }
                ResourceUpdate::DeleteBlobImage(key) => {
                    self.blob_image_templates.remove(&key);
                    if let Some(ref mut handler) = self.blob_image_handler {
                        handler.delete(key);
                    }
                }
                ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => {
                    self.update_blob_image(*key, None, None, None, area);
                    blobs_to_rasterize.push(*key);
                }
                ResourceUpdate::AddFont(ref font) => {
                    match font {
                        AddFont::Raw(key, bytes, index) => {
                            self.fonts.templates.insert(
                                *key,
                                FontTemplate::Raw(Arc::clone(bytes), *index),
                            );
                        }
                        AddFont::Native(key, native_font_handle) => {
                            self.fonts.templates.insert(
                                *key,
                                FontTemplate::Native(native_font_handle.clone()),
                            );
                        }
                    }
                }
                ResourceUpdate::AddFontInstance(ref instance) => {
                    // TODO(nical): Don't clone these.
                    self.fonts.instances.add_font_instance(
                        instance.key,
                        instance.font_key,
                        instance.glyph_size,
                        instance.options.clone(),
                        instance.platform_options.clone(),
                        instance.variations.clone(),
                    );
                }
                ResourceUpdate::DeleteFont(key) => {
                    self.fonts.templates.remove(&key);
                    if let Some(ref mut handler) = self.blob_image_handler {
                        handler.delete_font(key);
                    }
                }
                ResourceUpdate::DeleteFontInstance(key) => {
                    // We will delete from the shared font instance map in the resource cache
                    // after scene swap.

                    if let Some(ref mut r) = self.blob_image_handler {
                        r.delete_font_instance(key);
                    }
                }
                _ => {}
            }
        }

        let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize);
        transaction.blob_rasterizer = rasterizer;
        transaction.blob_requests = requests;
    }

    pub fn enable_multithreading(&mut self, enable: bool) {
        if let Some(ref mut handler) = self.blob_image_handler {
            handler.enable_multithreading(enable);
        }
    }

    fn update_blob_image(
        &mut self,
        key: BlobImageKey,
        descriptor: Option<&ImageDescriptor>,
        dirty_rect: Option<&BlobDirtyRect>,
        data: Option<Arc<BlobImageData>>,
        visible_rect: &DeviceIntRect,
    ) {
        if let Some(data) = data {
            let dirty_rect = dirty_rect.unwrap();
            self.blob_image_handler.as_mut().unwrap().update(key, data, visible_rect, dirty_rect);
        }

        let image = self.blob_image_templates
            .get_mut(&key)
            .expect("Attempt to update non-existent blob image");

        let mut valid_tiles_after_bounds_change = compute_valid_tiles_if_bounds_change(
            &image.visible_rect,
            visible_rect,
            image.tile_size,
        );

        match (image.valid_tiles_after_bounds_change, valid_tiles_after_bounds_change) {
            (Some(old), Some(ref mut new)) => {
                *new = new.intersection(&old).unwrap_or_else(TileRange::zero);
            }
            (Some(old), None) => {
                valid_tiles_after_bounds_change = Some(old);
            }
            _ => {}
        }

        let blob_size = visible_rect.size;

        if let Some(descriptor) = descriptor {
            image.descriptor = *descriptor;
        } else {
            // make sure the descriptor size matches the visible rect.
            // This might not be necessary but let's stay on the safe side.
            image.descriptor.size = blob_size;
        }

        if let Some(dirty_rect) = dirty_rect {
            image.dirty_rect = image.dirty_rect.union(dirty_rect);
        }

        image.valid_tiles_after_bounds_change = valid_tiles_after_bounds_change;
        image.visible_rect = *visible_rect;
    }

    pub fn create_blob_scene_builder_requests(
        &mut self,
        keys: &[BlobImageKey]
    ) -> (Option<Box<dyn AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) {
        if self.blob_image_handler.is_none() || keys.is_empty() {
            return (None, Vec::new());
        }

        let mut blob_request_params = Vec::new();
        for key in keys {
            let template = self.blob_image_templates.get_mut(key).unwrap();

            // If we know that only a portion of the blob image is in the viewport,
            // only request these visible tiles since blob images can be huge.
            let tiles = compute_tile_range(
                &template.visible_rect,
                template.tile_size,
            );

            // Don't request tiles that weren't invalidated.
            let dirty_tiles = match template.dirty_rect {
                DirtyRect::Partial(dirty_rect) => {
                    compute_tile_range(
                        &dirty_rect.cast_unit(),
                        template.tile_size,
                    )
                }
                DirtyRect::All => tiles,
            };

            for_each_tile_in_range(&tiles, |tile| {
                let still_valid = template.valid_tiles_after_bounds_change
                    .map(|valid_tiles| valid_tiles.contains(tile))
                    .unwrap_or(true);

                if still_valid && !dirty_tiles.contains(tile) {
                    return;
                }

                let descriptor = BlobImageDescriptor {
                    rect: compute_tile_rect(
                        &template.visible_rect,
                        template.tile_size,
                        tile,
                    ).cast_unit(),
                    format: template.descriptor.format,
                };

                assert!(descriptor.rect.size.width > 0 && descriptor.rect.size.height > 0);
                blob_request_params.push(
                    BlobImageParams {
                        request: BlobImageRequest { key: *key, tile },
                        descriptor,
                        dirty_rect: DirtyRect::All,
                    }
                );
            });

            template.dirty_rect = DirtyRect::empty();
            template.valid_tiles_after_bounds_change = None;
        }

        let handler = self.blob_image_handler.as_mut().unwrap();
        handler.prepare_resources(&self.fonts, &blob_request_params);
        (Some(handler.create_blob_rasterizer()), blob_request_params)
    }
}

fn compute_valid_tiles_if_bounds_change(
    prev_rect: &DeviceIntRect,
    new_rect: &DeviceIntRect,
    tile_size: u16,
) -> Option<TileRange> {
    let intersection = match prev_rect.intersection(new_rect) {
        Some(rect) => rect,
        None => {
            return Some(TileRange::zero());
        }
    };

    let left = prev_rect.min_x() != new_rect.min_x();
    let right = prev_rect.max_x() != new_rect.max_x();
    let top = prev_rect.min_y() != new_rect.min_y();
    let bottom = prev_rect.max_y() != new_rect.max_y();

    if !left && !right && !top && !bottom {
        // Bounds have not changed.
        return None;
    }

    let tw = 1.0 / (tile_size as f32);
    let th = 1.0 / (tile_size as f32);

    let tiles = intersection
        .cast::<f32>()
        .scale(tw, th);

    let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) };
    let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) };
    let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) };
    let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) };

    Some(TileRange {
        origin: point2(min_x as i32, min_y as i32),
        size: size2((max_x - min_x) as i32, (max_y - min_y) as i32),
    })
}
<|endoftext|>


<commit_before>use librp::errors;
use librp::compression;
use std::io::{Error};
use byteorder::{ReadBytesExt, LittleEndian};
use std::io::BufReader;
use std::boxed::Box;
use std::io::Cursor;
use std::fs::File;
use std::io::Read;
use std::mem;

use encoding::{Encoding, DecoderTrap};
use encoding::all::UTF_16LE;

#[derive(Debug)]
pub struct MamHeader{
    pub signature: u32,
    pub uncompressed_size: u32
}

pub struct PrefetchFile{
    pub filehandle: BufReader<Box<Read>>
}
impl PrefetchFile{
    pub fn new(prefetch_file: &str) -> Result<PrefetchFile,errors::PrefetchError> {
        // Check if file is a prefetch file
        let signature = prefetch_signature(prefetch_file)?;

        // Open a filehandle to the file
        let mut fh = File::open(prefetch_file)?;

        // Our buffer that holds the entire file
        let mut buffer: Vec<u8> = Vec::new();

        if signature == 72171853{
            // Get MAM header
            let header = read_mam_head(&mut fh)?;

            // create a buffer for the compressed data
            let mut compressed_buffer: Vec<u8> = Vec::new();
            // read from file into the compressed buffer
            fh.read_to_end(&mut compressed_buffer)?;

            // decompress the buffer
            let decompressed_buffer = match compression::decompress(
                                                    &compressed_buffer[..],
                                                    header.uncompressed_size as usize){
                Some(decompressed_buffer) => decompressed_buffer,
                None => return Err(errors::PrefetchError::decompression_error(
                    format!("Error decompressing file")))
            };

            buffer = decompressed_buffer.to_vec();
        } else {
            try!(fh.read_to_end(&mut buffer));
        }

        Ok(PrefetchFile {
            filehandle: BufReader::new(
                Box::new(Cursor::new(buffer))
            )
        })
    }

    pub fn get_file_header(&mut self) -> Result<PrefetchHeader,errors::PrefetchError>{
        let mut header: PrefetchHeader = unsafe {
            mem::zeroed()
        };

        header.version = self.filehandle.read_u32::<LittleEndian>()?;
        header.signature = self.filehandle.read_u32::<LittleEndian>()?;
        header.unknown1 = self.filehandle.read_u32::<LittleEndian>()?;
        header.filesize = self.filehandle.read_u32::<LittleEndian>()?;

        // Create a vector to store the byte buffer
        let mut name_buffer = [0u8; 60];
        self.filehandle.read_exact(&mut name_buffer)?;

        header.filename = match UTF_16LE.decode(&name_buffer,DecoderTrap::Ignore){
            Ok(filename) => filename,
            Err(error) => return Err(errors::PrefetchError::decode_error(
                                        format!("Error decoding filename in header. [{}]",error)))
        };
        let eo = match header.filename.find("\x00"){
            Some(eo) => eo,
            None => 30
        };

        if eo < 30 {
            header.filename.truncate(eo);
        }

        header.hash = self.filehandle.read_u32::<LittleEndian>()?;
        header.unknon2 = self.filehandle.read_u32::<LittleEndian>()?;

        Ok(header)
    }
}

#[derive(Debug)]
pub struct PrefetchHeader{
    pub version: u32,
    pub signature: u32,
    pub unknown1: u32,
    pub filesize: u32,
    pub filename: String,
    pub hash: u32,
    pub unknon2: u32
}

pub fn read_mam_head<R: Read>(mut buffer: R)->Result<MamHeader,Error>{
    let mut header: MamHeader = unsafe {
        mem::zeroed()
    };

    header.signature = buffer.read_u32::<LittleEndian>()?;
    header.uncompressed_size = buffer.read_u32::<LittleEndian>()?;

    Ok(header)
}

pub fn prefetch_signature(filename: &str)->Result<u32,errors::PrefetchError>{
    // Open a filehandle to the file
    let mut fh = File::open(filename)?;

    // Get File Size to ensure we have enough data to read
    let metadata = try!(fh.metadata());
    let file_size = metadata.len();

    if file_size > 8 {
        // We are only ever going to check the first 8 bytes of the file for a signature
        // check for MAM signature
        let prt1 = try!(fh.read_u32::<LittleEndian>());
        if prt1 == 72171853 {
            Ok(prt1)
        }else{
            // check for SCCA signature
            let prt2 = try!(fh.read_u32::<LittleEndian>());
            if prt2 == 1094927187 {
                Ok(prt2)
            } else {
                // Unknown File signature
                Err(errors::PrefetchError::invalid_file_signature(format!(
                        "Unkown File Signature"
                )))
            }
        }
    } else {
        Err(errors::PrefetchError::invalid_file_signature(format!("File size invalid")))
    }
}
<commit_msg>creating PrefetchFile to hold datastructures and PrefetchHandle to hold buffer<commit_after>use librp::errors;
use librp::compression;
use librp::fileinfo;
use std::io::{Error};
use byteorder::{ReadBytesExt, LittleEndian};
use std::io::BufReader;
use std::boxed::Box;
use std::io::Cursor;
use std::fs::File;
use std::io::Read;
use std::mem;

use encoding::{Encoding, DecoderTrap};
use encoding::all::UTF_16LE;

#[derive(Debug)]
pub struct MamHeader{
    pub signature: u32,
    pub uncompressed_size: u32
}

#[derive(Debug)]
pub struct PrefetchHandle{
    pub filename: String,
    pub buffer: Vec<u8>
}
impl PrefetchHandle{
    pub fn new(prefetch_filename: &str) -> Result<PrefetchHandle,errors::PrefetchError> {
        let mut prefetch_file: PrefetchHandle = unsafe {
            mem::zeroed()
        };
        prefetch_file.filename = String::from(prefetch_filename);

        // Check if file is a prefetch file
        let signature = prefetch_signature(prefetch_filename)?;

        // Open a filehandle to the file
        let mut fh = File::open(prefetch_filename)?;

        if signature == 72171853{
            // Get MAM header
            let header = read_mam_head(&mut fh)?;

            // create a buffer for the compressed data
            let mut compressed_buffer: Vec<u8> = Vec::new();
            // read from file into the compressed buffer
            fh.read_to_end(&mut compressed_buffer)?;

            // decompress the buffer
            let decompressed_buffer = match compression::decompress(
                                                    &compressed_buffer[..],
                                                    header.uncompressed_size as usize){
                Some(decompressed_buffer) => decompressed_buffer,
                None => return Err(errors::PrefetchError::decompression_error(
                    format!("Error decompressing file")))
            };

            prefetch_file.buffer = decompressed_buffer.to_vec();
        } else {
            try!(fh.read_to_end(&mut prefetch_file.buffer));
        }

        Ok(
            prefetch_file
        )
    }

    pub fn get_prefetch(&self) -> Result<PrefetchFile,errors::PrefetchError> {
        Ok(
            PrefetchFile::new(&self.buffer)?
        )
    }
}

#[derive(Debug)]
pub struct PrefetchFile{
    pub header: PrefetchHeader
}
impl PrefetchFile{
    pub fn new(buffer: &Vec<u8>) -> Result<PrefetchFile,errors::PrefetchError> {
        let prefetch_header = PrefetchHeader::new(buffer.as_slice())?;

        Ok(
            PrefetchFile{
                header: prefetch_header
            }
        )
    }

    // pub fn parse_file_info(&mut self) -> Result<fileinfo::FileInformation,errors::PrefetchError> {
    //     if self.header.version == 17{
    //         let file_info: fileinfo::FileInformationV17 = unsafe {
    //             mem::zeroed()
    //         };
    //         Ok(
    //             fileinfo::FileInformation::v17(
    //                 file_info
    //             )
    //         )
    //     } else if self.header.version == 23{
    //         let file_info: fileinfo::FileInformationV23 = unsafe {
    //             mem::zeroed()
    //         };
    //         Ok(
    //             fileinfo::FileInformation::v23(
    //                 file_info
    //             )
    //         )
    //     } else if self.header.version == 26{
    //         let file_info: fileinfo::FileInformationV26 = unsafe {
    //             mem::zeroed()
    //         };
    //         Ok(
    //             fileinfo::FileInformation::v26(
    //                 file_info
    //             )
    //         )
    //     } else if self.header.version == 30{
    //         let file_info: fileinfo::FileInformationV30 = unsafe {
    //             mem::zeroed()
    //         };
    //         Ok(
    //             fileinfo::FileInformation::v30(
    //                 file_info
    //             )
    //         )
    //     } else {
    //         Err(errors::PrefetchError::parse_error(
    //             format!("Error parsing file info. Invalid version: {}",self.header.version)
    //         ))
    //     }
    // }
}

#[derive(Debug)]
pub struct PrefetchHeader{
    pub version: u32,
    pub signature: u32,
    pub unknown1: u32,
    pub filesize: u32,
    pub filename: String,
    pub hash: u32,
    pub unknon2: u32
}
impl PrefetchHeader{
    pub fn new<R: Read>(mut buffer: R)->Result<PrefetchHeader,errors::PrefetchError>{
        let mut header: PrefetchHeader = unsafe {
            mem::zeroed()
        };
        header.version = buffer.read_u32::<LittleEndian>()?;
        header.signature = buffer.read_u32::<LittleEndian>()?;
        header.unknown1 = buffer.read_u32::<LittleEndian>()?;
        header.filesize = buffer.read_u32::<LittleEndian>()?;

        // Create a vector to store the byte buffer
        let mut name_buffer = [0u8; 60];
        buffer.read_exact(&mut name_buffer)?;

        header.filename = match UTF_16LE.decode(&name_buffer,DecoderTrap::Ignore){
            Ok(filename) => filename,
            Err(error) => return Err(errors::PrefetchError::decode_error(
                                        format!("Error decoding filename in header. [{}]",error)))
        };
        let eo = match header.filename.find("\x00"){
            Some(eo) => eo,
            None => 30
        };

        if eo < 30 {
            header.filename.truncate(eo);
        }

        header.hash = buffer.read_u32::<LittleEndian>()?;
        header.unknon2 = buffer.read_u32::<LittleEndian>()?;

        Ok(header)
    }
}

pub fn read_mam_head<R: Read>(mut buffer: R)->Result<MamHeader,Error>{
    let mut header: MamHeader = unsafe {
        mem::zeroed()
    };

    header.signature = buffer.read_u32::<LittleEndian>()?;
    header.uncompressed_size = buffer.read_u32::<LittleEndian>()?;

    Ok(header)
}

pub fn prefetch_signature(filename: &str)->Result<u32,errors::PrefetchError>{
    // Open a filehandle to the file
    let mut fh = File::open(filename)?;

    // Get File Size to ensure we have enough data to read
    let metadata = try!(fh.metadata());
    let file_size = metadata.len();

    if file_size > 8 {
        // We are only ever going to check the first 8 bytes of the file for a signature
        // check for MAM signature
        let prt1 = try!(fh.read_u32::<LittleEndian>());
        if prt1 == 72171853 {
            Ok(prt1)
        }else{
            // check for SCCA signature
            let prt2 = try!(fh.read_u32::<LittleEndian>());
            if prt2 == 1094927187 {
                Ok(prt2)
            } else {
                // Unknown File signature
                Err(errors::PrefetchError::invalid_file_signature(format!(
                        "Unkown File Signature"
                )))
            }
        }
    } else {
        Err(errors::PrefetchError::invalid_file_signature(format!("File size invalid")))
    }
}
<|endoftext|>


<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.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use old_io::{IoResult, File};
    use old_path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::Ok;
    use slice::SliceExt;
    use mem;
    use os::errno;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;
        #[cfg(target_arch = "powerpc")]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(&mut v[read..]);
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as usize;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use old_io::{IoResult};
    use marker::Sync;
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    unsafe impl Sync for *const SecRandom {}

    #[allow(non_upper_case_globals)]
    static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use old_io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in 0..20 {
            let (tx, rx) = channel();
            txs.push(tx);

            thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in 0..100 {
                    r.next_u32();
                    thread::yield_now();
                    r.next_u64();
                    thread::yield_now();
                    r.fill_bytes(&mut v);
                    thread::yield_now();
                }
            });
        }

        // start all the tasks
        for tx in &txs {
            tx.send(()).unwrap();
        }
    }
}
<commit_msg>iOS: Sync/Send fallout<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.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use old_io::{IoResult, File};
    use old_path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::Ok;
    use slice::SliceExt;
    use mem;
    use os::errno;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;
        #[cfg(target_arch = "powerpc")]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(&mut v[read..]);
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as usize;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use old_io::{IoResult};
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    #[allow(non_upper_case_globals)]
    const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use old_io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in 0..20 {
            let (tx, rx) = channel();
            txs.push(tx);

            thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in 0..100 {
                    r.next_u32();
                    thread::yield_now();
                    r.next_u64();
                    thread::yield_now();
                    r.fill_bytes(&mut v);
                    thread::yield_now();
                }
            });
        }

        // start all the tasks
        for tx in &txs {
            tx.send(()).unwrap();
        }
    }
}
<|endoftext|>


<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.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use io::{IoResult, File};
    use path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::Ok;
    use slice::SliceExt;
    use mem;
    use os::errno;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(v.slice_from_mut(read));
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as uint;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use io::{IoResult};
    use marker::Sync;
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    #[allow(missing_copy_implementations)]
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    unsafe impl Sync for *const SecRandom {}

    #[allow(non_upper_case_globals)]
    static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread::Thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in range(0u, 20) {
            let (tx, rx) = channel();
            txs.push(tx);

            Thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                Thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in range(0u, 100) {
                    r.next_u32();
                    Thread::yield_now();
                    r.next_u64();
                    Thread::yield_now();
                    r.fill_bytes(&mut v);
                    Thread::yield_now();
                }
            });
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<commit_msg>auto merge of #20980 : richo/rust/final-power, r=alexcrichton<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.

//! Interfaces to the operating system provided random number
//! generators.

pub use self::imp::OsRng;

#[cfg(all(unix, not(target_os = "ios")))]
mod imp {
    extern crate libc;

    use self::OsRngInner::*;

    use io::{IoResult, File};
    use path::Path;
    use rand::Rng;
    use rand::reader::ReaderRng;
    use result::Result::Ok;
    use slice::SliceExt;
    use mem;
    use os::errno;

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        extern "C" {
            fn syscall(number: libc::c_long, ...) -> libc::c_long;
        }

        #[cfg(target_arch = "x86_64")]
        const NR_GETRANDOM: libc::c_long = 318;
        #[cfg(target_arch = "x86")]
        const NR_GETRANDOM: libc::c_long = 355;
        #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
        const NR_GETRANDOM: libc::c_long = 384;
        #[cfg(target_arch = "powerpc")]
        const NR_GETRANDOM: libc::c_long = 384;

        unsafe {
            syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0u)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) {
        let mut read = 0;
        let len = v.len();
        while read < len {
            let result = getrandom(v.slice_from_mut(read));
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as uint;
            }
        }
    }

    fn getrandom_next_u32() -> u32 {
        let mut buf: [u8; 4] = [0u8; 4];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 4], u32>(buf) }
    }

    fn getrandom_next_u64() -> u64 {
        let mut buf: [u8; 8] = [0u8; 8];
        getrandom_fill_bytes(&mut buf);
        unsafe { mem::transmute::<[u8; 8], u64>(buf) }
    }

    #[cfg(all(target_os = "linux",
              any(target_arch = "x86_64",
                  target_arch = "x86",
                  target_arch = "arm",
                  target_arch = "aarch64",
                  target_arch = "powerpc")))]
    fn is_getrandom_available() -> bool {
        use sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};

        static GETRANDOM_CHECKED: AtomicBool = ATOMIC_BOOL_INIT;
        static GETRANDOM_AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;

        if !GETRANDOM_CHECKED.load(Ordering::Relaxed) {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = errno() as libc::c_int;
                err != libc::ENOSYS
            } else {
                true
            };
            GETRANDOM_AVAILABLE.store(available, Ordering::Relaxed);
            GETRANDOM_CHECKED.store(true, Ordering::Relaxed);
            available
        } else {
            GETRANDOM_AVAILABLE.load(Ordering::Relaxed)
        }
    }

    #[cfg(not(all(target_os = "linux",
                  any(target_arch = "x86_64",
                      target_arch = "x86",
                      target_arch = "arm",
                      target_arch = "aarch64",
                      target_arch = "powerpc"))))]
    fn is_getrandom_available() -> bool { false }

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        inner: OsRngInner,
    }

    enum OsRngInner {
        OsGetrandomRng,
        OsReaderRng(ReaderRng<File>),
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            if is_getrandom_available() {
                return Ok(OsRng { inner: OsGetrandomRng });
            }

            let reader = try!(File::open(&Path::new("/dev/urandom")));
            let reader_rng = ReaderRng::new(reader);

            Ok(OsRng { inner: OsReaderRng(reader_rng) })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u32(),
                OsReaderRng(ref mut rng) => rng.next_u32(),
            }
        }
        fn next_u64(&mut self) -> u64 {
            match self.inner {
                OsGetrandomRng => getrandom_next_u64(),
                OsReaderRng(ref mut rng) => rng.next_u64(),
            }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            match self.inner {
                OsGetrandomRng => getrandom_fill_bytes(v),
                OsReaderRng(ref mut rng) => rng.fill_bytes(v)
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    extern crate libc;

    use io::{IoResult};
    use marker::Sync;
    use mem;
    use os;
    use rand::Rng;
    use result::Result::{Ok};
    use self::libc::{c_int, size_t};
    use slice::SliceExt;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    #[allow(missing_copy_implementations)]
    pub struct OsRng {
        // dummy field to ensure that this struct cannot be constructed outside of this module
        _dummy: (),
    }

    #[repr(C)]
    struct SecRandom;

    unsafe impl Sync for *const SecRandom {}

    #[allow(non_upper_case_globals)]
    static kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;

    #[link(name = "Security", kind = "framework")]
    extern "C" {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t, bytes: *mut u8) -> c_int;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            Ok(OsRng { _dummy: () })
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                SecRandomCopyBytes(kSecRandomDefault, v.len() as size_t, v.as_mut_ptr())
            };
            if ret == -1 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }
}

#[cfg(windows)]
mod imp {
    extern crate libc;

    use io::{IoResult, IoError};
    use mem;
    use ops::Drop;
    use os;
    use rand::Rng;
    use result::Result::{Ok, Err};
    use self::libc::{DWORD, BYTE, LPCSTR, BOOL};
    use self::libc::types::os::arch::extra::{LONG_PTR};
    use slice::SliceExt;

    type HCRYPTPROV = LONG_PTR;

    /// A random number generator that retrieves randomness straight from
    /// the operating system. Platform sources:
    ///
    /// - Unix-like systems (Linux, Android, Mac OSX): read directly from
    ///   `/dev/urandom`, or from `getrandom(2)` system call if available.
    /// - Windows: calls `CryptGenRandom`, using the default cryptographic
    ///   service provider with the `PROV_RSA_FULL` type.
    /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed.
    ///
    /// This does not block.
    pub struct OsRng {
        hcryptprov: HCRYPTPROV
    }

    static PROV_RSA_FULL: DWORD = 1;
    static CRYPT_SILENT: DWORD = 64;
    static CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;

    #[allow(non_snake_case)]
    extern "system" {
        fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
                                pszContainer: LPCSTR,
                                pszProvider: LPCSTR,
                                dwProvType: DWORD,
                                dwFlags: DWORD) -> BOOL;
        fn CryptGenRandom(hProv: HCRYPTPROV,
                          dwLen: DWORD,
                          pbBuffer: *mut BYTE) -> BOOL;
        fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL;
    }

    impl OsRng {
        /// Create a new `OsRng`.
        pub fn new() -> IoResult<OsRng> {
            let mut hcp = 0;
            let ret = unsafe {
                CryptAcquireContextA(&mut hcp, 0 as LPCSTR, 0 as LPCSTR,
                                     PROV_RSA_FULL,
                                     CRYPT_VERIFYCONTEXT | CRYPT_SILENT)
            };

            if ret == 0 {
                Err(IoError::last_error())
            } else {
                Ok(OsRng { hcryptprov: hcp })
            }
        }
    }

    impl Rng for OsRng {
        fn next_u32(&mut self) -> u32 {
            let mut v = [0u8; 4];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn next_u64(&mut self) -> u64 {
            let mut v = [0u8; 8];
            self.fill_bytes(&mut v);
            unsafe { mem::transmute(v) }
        }
        fn fill_bytes(&mut self, v: &mut [u8]) {
            let ret = unsafe {
                CryptGenRandom(self.hcryptprov, v.len() as DWORD,
                               v.as_mut_ptr())
            };
            if ret == 0 {
                panic!("couldn't generate random bytes: {}", os::last_os_error());
            }
        }
    }

    impl Drop for OsRng {
        fn drop(&mut self) {
            let ret = unsafe {
                CryptReleaseContext(self.hcryptprov, 0)
            };
            if ret == 0 {
                panic!("couldn't release context: {}", os::last_os_error());
            }
        }
    }
}

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

    use sync::mpsc::channel;
    use rand::Rng;
    use super::OsRng;
    use thread::Thread;

    #[test]
    fn test_os_rng() {
        let mut r = OsRng::new().unwrap();

        r.next_u32();
        r.next_u64();

        let mut v = [0u8; 1000];
        r.fill_bytes(&mut v);
    }

    #[test]
    fn test_os_rng_tasks() {

        let mut txs = vec!();
        for _ in range(0u, 20) {
            let (tx, rx) = channel();
            txs.push(tx);

            Thread::spawn(move|| {
                // wait until all the tasks are ready to go.
                rx.recv().unwrap();

                // deschedule to attempt to interleave things as much
                // as possible (XXX: is this a good test?)
                let mut r = OsRng::new().unwrap();
                Thread::yield_now();
                let mut v = [0u8; 1000];

                for _ in range(0u, 100) {
                    r.next_u32();
                    Thread::yield_now();
                    r.next_u64();
                    Thread::yield_now();
                    r.fill_bytes(&mut v);
                    Thread::yield_now();
                }
            });
        }

        // start all the tasks
        for tx in txs.iter() {
            tx.send(()).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>extern crate moho;
extern crate sdl2_image;
extern crate sdl2;

use self::sdl2::event::Event;
use self::sdl2::keyboard::Keycode;
use self::sdl2::rect;

use self::sdl2_image::{LoadTexture, INIT_PNG, INIT_JPG};
use std::error::Error;
use std::path::Path;

pub fn run() -> Result<(), Box<Error>> {
    const WINDOW_HEIGHT: u32 = 600;
    const WINDOW_WIDTH: u32 = 800;
    let (mut renderer, mut event_pump) =
        try!(moho::init("Master Smasher", WINDOW_WIDTH, WINDOW_HEIGHT));
    let _image_context = try!(sdl2_image::init(INIT_PNG | INIT_JPG));
    let mut input_manager = moho::input_manager::InputManager::new();
    let background_path = Path::new("resources/background_game.png");
    let meteor_path = Path::new("resources/meteor.png");
    let background_texture = try!(renderer.load_texture(background_path));
    let meteor_texture = try!(renderer.load_texture(meteor_path));
    let query = meteor_texture.query();
    let mut dst = rect::Rect::new(0, 0, query.width, query.height);
    let mut dst2 = None;

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Some(key), .. } => {
                    input_manager.press_key(key);
                }
                Event::KeyUp { keycode: Some(key), .. } => {
                    input_manager.release_key(key);
                }
                _ => {}
            }
        }

        if input_manager.is_key_down(Keycode::Escape) {
            break;
        }

        if input_manager.is_key_down(Keycode::Down) {
            dst.offset(0, 1);

            if (dst.top() > WINDOW_HEIGHT as i32) {
                let top = dst.top() % WINDOW_HEIGHT as i32;
                dst.set_y(top);
                dst2 = None
            }

            if (dst.bottom() > WINDOW_HEIGHT as i32) {
                let mut clone_rect = dst.clone();
                clone_rect.set_bottom(dst.bottom() % WINDOW_HEIGHT as i32);
                dst2 = Some(clone_rect);
            }
        }

        if input_manager.is_key_down(Keycode::Up) {
            dst.offset(0, -1);
        }

        if input_manager.is_key_down(Keycode::Left) {
            dst.offset(-1, 0);
        }

        if input_manager.is_key_down(Keycode::Right) {
            dst.offset(1, 0);
        }

        // The rest of the game loop goes here...
        renderer.clear();
        try!(renderer.copy(&background_texture, None, None));
        try!(renderer.copy(&meteor_texture, None, Some(dst)));

        match dst2 {
            Some(_) => {
                try!(renderer.copy(&meteor_texture, None, dst2));
            }
            None => {}
        }

        renderer.present();
    }

    Ok(())
}
<commit_msg>split meteor and bg into objects<commit_after>extern crate moho;
extern crate sdl2_image;
extern crate sdl2;
extern crate glm;

use self::sdl2::event::Event;
use self::sdl2::keyboard::Keycode;
use self::sdl2::render::{Renderer, Texture};
use self::sdl2::rect;

use self::sdl2_image::{LoadTexture, INIT_PNG, INIT_JPG};
use std::error::Error;
use std::path::Path;

pub fn run() -> Result<(), Box<Error>> {
    const WINDOW_HEIGHT: u32 = 600;
    const WINDOW_WIDTH: u32 = 800;

    let (mut renderer, mut event_pump) =
        try!(moho::init("Master Smasher", WINDOW_WIDTH, WINDOW_HEIGHT));
    let _image_context = try!(sdl2_image::init(INIT_PNG | INIT_JPG));
    let mut input_manager = moho::input_manager::InputManager::new();
    let background_path = Path::new("resources/background_game.png");
    let meteor_path = Path::new("resources/meteor.png");

    let background = Background { texture: try!(renderer.load_texture(background_path)) };
    let mut meteor = Meteor::new(try!(renderer.load_texture(meteor_path)),
                                 glm::uvec2(WINDOW_WIDTH, WINDOW_HEIGHT));

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Some(key), .. } => {
                    input_manager.press_key(key);
                }
                Event::KeyUp { keycode: Some(key), .. } => {
                    input_manager.release_key(key);
                }
                _ => {}
            }
        }
        if input_manager.is_key_down(Keycode::Escape) {
            break;
        }
        meteor.update(&input_manager);

        renderer.clear();
        try!(background.draw(&mut renderer));
        try!(meteor.draw(&mut renderer));
        renderer.present();
    }

    Ok(())
}

struct Background {
    texture: Texture,
}

impl Background {
    fn draw(&self, renderer: &mut Renderer) -> Result<(), Box<Error>> {
        try!(renderer.copy(&self.texture, None, None));

        Ok(())
    }
}

struct Meteor {
    texture: Texture,
    rect: rect::Rect,
    wrapping_rect: rect::Rect,
}

impl Meteor {
    fn new(texture: Texture, max_coords: glm::Vector2<u32>) -> Self {
        let query = texture.query();
        Meteor {
            texture: texture,
            rect: rect::Rect::new(0, 0, query.width, query.height),
            wrapping_rect: rect::Rect::new(0, 0, max_coords.x, max_coords.y),
        }
    }

    fn update(&mut self, input_manager: &moho::input_manager::InputManager) {
        let mut top = self.rect.y();
        let mut left = self.rect.x();

        if input_manager.is_key_down(Keycode::Down) {
            top += 1;
        }

        if input_manager.is_key_down(Keycode::Up) {
            top -= 1;
        }

        if input_manager.is_key_down(Keycode::Left) {
            left -= 1;
        }

        if input_manager.is_key_down(Keycode::Right) {
            left += 1;
        }

        let max_height = self.wrapping_rect.height() as i32;
        let max_width = self.wrapping_rect.width() as i32;

        self.rect.set_y((top + max_height) % max_height);
        self.rect.set_x((left + max_width) % max_width);
    }

    fn draw(&self, renderer: &mut Renderer) -> Result<(), Box<Error>> {
        try!(renderer.copy(&self.texture, None, Some(self.rect)));

        let mut optional_rect: Option<rect::Rect> = None;
        match optional_rect {
            Some(_) => {
                try!(renderer.copy(&self.texture, None, optional_rect));
            }
            None => {}
        };

        Ok(())
    }
}
<|endoftext|>