<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>Auto merge of #4066 - moz-gfx:github-sync, r=auto<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 std::cell::Cell;
use std::fmt::Write;
use std::rc::Rc;
use std::time::Duration;
use std::{fmt, net};

use actix_rt::{
    task::JoinHandle,
    time::{interval, sleep_until, Instant, Sleep},
};
use bytes::BytesMut;
use time::OffsetDateTime;

/// "Sun, 06 Nov 1994 08:49:37 GMT".len()
const DATE_VALUE_LENGTH: usize = 29;

#[derive(Debug, PartialEq, Clone, Copy)]
/// Server keep-alive setting
pub enum KeepAlive {
    /// Keep alive in seconds
    Timeout(usize),
    /// Rely on OS to shutdown tcp connection
    Os,
    /// Disabled
    Disabled,
}

impl From<usize> for KeepAlive {
    fn from(keepalive: usize) -> Self {
        KeepAlive::Timeout(keepalive)
    }
}

impl From<Option<usize>> for KeepAlive {
    fn from(keepalive: Option<usize>) -> Self {
        if let Some(keepalive) = keepalive {
            KeepAlive::Timeout(keepalive)
        } else {
            KeepAlive::Disabled
        }
    }
}

/// Http service configuration
pub struct ServiceConfig(Rc<Inner>);

struct Inner {
    keep_alive: Option<Duration>,
    client_timeout: u64,
    client_disconnect: u64,
    ka_enabled: bool,
    secure: bool,
    local_addr: Option<std::net::SocketAddr>,
    date_service: DateService,
}

impl Clone for ServiceConfig {
    fn clone(&self) -> Self {
        ServiceConfig(self.0.clone())
    }
}

impl Default for ServiceConfig {
    fn default() -> Self {
        Self::new(KeepAlive::Timeout(5), 0, 0, false, None)
    }
}

impl ServiceConfig {
    /// Create instance of `ServiceConfig`
    pub fn new(
        keep_alive: KeepAlive,
        client_timeout: u64,
        client_disconnect: u64,
        secure: bool,
        local_addr: Option<net::SocketAddr>,
    ) -> ServiceConfig {
        let (keep_alive, ka_enabled) = match keep_alive {
            KeepAlive::Timeout(val) => (val as u64, true),
            KeepAlive::Os => (0, true),
            KeepAlive::Disabled => (0, false),
        };
        let keep_alive = if ka_enabled && keep_alive > 0 {
            Some(Duration::from_secs(keep_alive))
        } else {
            None
        };

        ServiceConfig(Rc::new(Inner {
            keep_alive,
            ka_enabled,
            client_timeout,
            client_disconnect,
            secure,
            local_addr,
            date_service: DateService::new(),
        }))
    }

    /// Returns true if connection is secure (HTTPS)
    #[inline]
    pub fn secure(&self) -> bool {
        self.0.secure
    }

    /// Returns the local address that this server is bound to.
    #[inline]
    pub fn local_addr(&self) -> Option<net::SocketAddr> {
        self.0.local_addr
    }

    /// Keep alive duration if configured.
    #[inline]
    pub fn keep_alive(&self) -> Option<Duration> {
        self.0.keep_alive
    }

    /// Return state of connection keep-alive functionality
    #[inline]
    pub fn keep_alive_enabled(&self) -> bool {
        self.0.ka_enabled
    }

    /// Client timeout for first request.
    #[inline]
    pub fn client_timer(&self) -> Option<Sleep> {
        let delay_time = self.0.client_timeout;
        if delay_time != 0 {
            Some(sleep_until(
                self.0.date_service.now() + Duration::from_millis(delay_time),
            ))
        } else {
            None
        }
    }

    /// Client timeout for first request.
    pub fn client_timer_expire(&self) -> Option<Instant> {
        let delay = self.0.client_timeout;
        if delay != 0 {
            Some(self.0.date_service.now() + Duration::from_millis(delay))
        } else {
            None
        }
    }

    /// Client disconnect timer
    pub fn client_disconnect_timer(&self) -> Option<Instant> {
        let delay = self.0.client_disconnect;
        if delay != 0 {
            Some(self.0.date_service.now() + Duration::from_millis(delay))
        } else {
            None
        }
    }

    #[inline]
    /// Return keep-alive timer delay is configured.
    pub fn keep_alive_timer(&self) -> Option<Sleep> {
        if let Some(ka) = self.0.keep_alive {
            Some(sleep_until(self.0.date_service.now() + ka))
        } else {
            None
        }
    }

    /// Keep-alive expire time
    pub fn keep_alive_expire(&self) -> Option<Instant> {
        if let Some(ka) = self.0.keep_alive {
            Some(self.0.date_service.now() + ka)
        } else {
            None
        }
    }

    #[inline]
    pub(crate) fn now(&self) -> Instant {
        self.0.date_service.now()
    }

    #[doc(hidden)]
    pub fn set_date(&self, dst: &mut BytesMut) {
        let mut buf: [u8; 39] = [0; 39];
        buf[..6].copy_from_slice(b"date: ");
        self.0
            .date_service
            .set_date(|date| buf[6..35].copy_from_slice(&date.bytes));
        buf[35..].copy_from_slice(b"\r\n\r\n");
        dst.extend_from_slice(&buf);
    }

    pub(crate) fn set_date_header(&self, dst: &mut BytesMut) {
        self.0
            .date_service
            .set_date(|date| dst.extend_from_slice(&date.bytes));
    }
}

#[derive(Copy, Clone)]
struct Date {
    bytes: [u8; DATE_VALUE_LENGTH],
    pos: usize,
}

impl Date {
    fn new() -> Date {
        let mut date = Date {
            bytes: [0; DATE_VALUE_LENGTH],
            pos: 0,
        };
        date.update();
        date
    }

    fn update(&mut self) {
        self.pos = 0;
        write!(
            self,
            "{}",
            OffsetDateTime::now_utc().format("%a, %d %b %Y %H:%M:%S GMT")
        )
        .unwrap();
    }
}

impl fmt::Write for Date {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let len = s.len();
        self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
        self.pos += len;
        Ok(())
    }
}

/// Service for update Date and Instant periodically at 500 millis interval.
struct DateService {
    current: Rc<Cell<(Date, Instant)>>,
    handle: JoinHandle<()>,
}

impl Drop for DateService {
    fn drop(&mut self) {
        // stop the timer update async task on drop.
        self.handle.abort();
    }
}

impl DateService {
    fn new() -> Self {
        // shared date and timer for DateService and update async task.
        let current = Rc::new(Cell::new((Date::new(), Instant::now())));
        let current_clone = Rc::clone(&current);
        // spawn an async task sleep for 500 milli and update current date/timer in a loop.
        // handle is used to stop the task on DateService drop.
        let handle = actix_rt::spawn(async move {
            #[cfg(test)]
            let _notify = notify_on_drop::NotifyOnDrop::new();

            let mut interval = interval(Duration::from_millis(500));
            loop {
                let now = interval.tick().await;
                let date = Date::new();
                current_clone.set((date, now));
            }
        });

        DateService { current, handle }
    }

    fn now(&self) -> Instant {
        self.current.get().1
    }

    fn set_date<F: FnMut(&Date)>(&self, mut f: F) {
        f(&self.current.get().0);
    }
}

// test drop behavior of DateService. only enabled in tests.
// TODO: move to a util module for testing all spawn handle drop style tasks.
#[cfg(test)]
mod notify_on_drop {
    use std::sync::atomic::{AtomicBool, Ordering::SeqCst};

    static NOTIFY_DROPPED: AtomicBool = AtomicBool::new(false);

    pub(crate) fn is_dropped() -> bool {
        NOTIFY_DROPPED.load(SeqCst)
    }

    pub(crate) struct NotifyOnDrop;

    impl NotifyOnDrop {
        pub(crate) fn new() -> Self {
            NOTIFY_DROPPED.store(false, SeqCst);
            NotifyOnDrop
        }
    }

    impl Drop for NotifyOnDrop {
        fn drop(&mut self) {
            NOTIFY_DROPPED.store(true, SeqCst);
        }
    }
}

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

    use actix_rt::task::yield_now;

    #[actix_rt::test]
    async fn test_date_service_update() {
        let settings = ServiceConfig::new(KeepAlive::Os, 0, 0, false, None);

        yield_now().await;

        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf1);
        let now1 = settings.now();

        sleep_until(Instant::now() + Duration::from_secs(2)).await;
        yield_now().await;

        let now2 = settings.now();
        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf2);

        assert_ne!(now1, now2);

        assert_ne!(buf1, buf2);

        drop(settings);
        assert!(notify_on_drop::is_dropped());
    }

    #[actix_rt::test]
    async fn test_date_service_drop() {
        let service = Rc::new(DateService::new());

        // yield so date service have a chance to register the spawned timer update task.
        yield_now().await;

        let clone1 = service.clone();
        let clone2 = service.clone();
        let clone3 = service.clone();

        drop(clone1);
        assert_eq!(false, notify_on_drop::is_dropped());
        drop(clone2);
        assert_eq!(false, notify_on_drop::is_dropped());
        drop(clone3);
        assert_eq!(false, notify_on_drop::is_dropped());

        drop(service);
        assert!(notify_on_drop::is_dropped());
    }

    #[test]
    fn test_date_len() {
        assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
    }

    #[actix_rt::test]
    async fn test_date() {
        let settings = ServiceConfig::new(KeepAlive::Os, 0, 0, false, None);
        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf1);
        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf2);
        assert_eq!(buf1, buf2);
    }
}
<commit_msg>fix notify on drop (#1987)<commit_after>use std::cell::Cell;
use std::fmt::Write;
use std::rc::Rc;
use std::time::Duration;
use std::{fmt, net};

use actix_rt::{
    task::JoinHandle,
    time::{interval, sleep_until, Instant, Sleep},
};
use bytes::BytesMut;
use time::OffsetDateTime;

/// "Sun, 06 Nov 1994 08:49:37 GMT".len()
const DATE_VALUE_LENGTH: usize = 29;

#[derive(Debug, PartialEq, Clone, Copy)]
/// Server keep-alive setting
pub enum KeepAlive {
    /// Keep alive in seconds
    Timeout(usize),
    /// Rely on OS to shutdown tcp connection
    Os,
    /// Disabled
    Disabled,
}

impl From<usize> for KeepAlive {
    fn from(keepalive: usize) -> Self {
        KeepAlive::Timeout(keepalive)
    }
}

impl From<Option<usize>> for KeepAlive {
    fn from(keepalive: Option<usize>) -> Self {
        if let Some(keepalive) = keepalive {
            KeepAlive::Timeout(keepalive)
        } else {
            KeepAlive::Disabled
        }
    }
}

/// Http service configuration
pub struct ServiceConfig(Rc<Inner>);

struct Inner {
    keep_alive: Option<Duration>,
    client_timeout: u64,
    client_disconnect: u64,
    ka_enabled: bool,
    secure: bool,
    local_addr: Option<std::net::SocketAddr>,
    date_service: DateService,
}

impl Clone for ServiceConfig {
    fn clone(&self) -> Self {
        ServiceConfig(self.0.clone())
    }
}

impl Default for ServiceConfig {
    fn default() -> Self {
        Self::new(KeepAlive::Timeout(5), 0, 0, false, None)
    }
}

impl ServiceConfig {
    /// Create instance of `ServiceConfig`
    pub fn new(
        keep_alive: KeepAlive,
        client_timeout: u64,
        client_disconnect: u64,
        secure: bool,
        local_addr: Option<net::SocketAddr>,
    ) -> ServiceConfig {
        let (keep_alive, ka_enabled) = match keep_alive {
            KeepAlive::Timeout(val) => (val as u64, true),
            KeepAlive::Os => (0, true),
            KeepAlive::Disabled => (0, false),
        };
        let keep_alive = if ka_enabled && keep_alive > 0 {
            Some(Duration::from_secs(keep_alive))
        } else {
            None
        };

        ServiceConfig(Rc::new(Inner {
            keep_alive,
            ka_enabled,
            client_timeout,
            client_disconnect,
            secure,
            local_addr,
            date_service: DateService::new(),
        }))
    }

    /// Returns true if connection is secure (HTTPS)
    #[inline]
    pub fn secure(&self) -> bool {
        self.0.secure
    }

    /// Returns the local address that this server is bound to.
    #[inline]
    pub fn local_addr(&self) -> Option<net::SocketAddr> {
        self.0.local_addr
    }

    /// Keep alive duration if configured.
    #[inline]
    pub fn keep_alive(&self) -> Option<Duration> {
        self.0.keep_alive
    }

    /// Return state of connection keep-alive functionality
    #[inline]
    pub fn keep_alive_enabled(&self) -> bool {
        self.0.ka_enabled
    }

    /// Client timeout for first request.
    #[inline]
    pub fn client_timer(&self) -> Option<Sleep> {
        let delay_time = self.0.client_timeout;
        if delay_time != 0 {
            Some(sleep_until(
                self.0.date_service.now() + Duration::from_millis(delay_time),
            ))
        } else {
            None
        }
    }

    /// Client timeout for first request.
    pub fn client_timer_expire(&self) -> Option<Instant> {
        let delay = self.0.client_timeout;
        if delay != 0 {
            Some(self.0.date_service.now() + Duration::from_millis(delay))
        } else {
            None
        }
    }

    /// Client disconnect timer
    pub fn client_disconnect_timer(&self) -> Option<Instant> {
        let delay = self.0.client_disconnect;
        if delay != 0 {
            Some(self.0.date_service.now() + Duration::from_millis(delay))
        } else {
            None
        }
    }

    #[inline]
    /// Return keep-alive timer delay is configured.
    pub fn keep_alive_timer(&self) -> Option<Sleep> {
        if let Some(ka) = self.0.keep_alive {
            Some(sleep_until(self.0.date_service.now() + ka))
        } else {
            None
        }
    }

    /// Keep-alive expire time
    pub fn keep_alive_expire(&self) -> Option<Instant> {
        if let Some(ka) = self.0.keep_alive {
            Some(self.0.date_service.now() + ka)
        } else {
            None
        }
    }

    #[inline]
    pub(crate) fn now(&self) -> Instant {
        self.0.date_service.now()
    }

    #[doc(hidden)]
    pub fn set_date(&self, dst: &mut BytesMut) {
        let mut buf: [u8; 39] = [0; 39];
        buf[..6].copy_from_slice(b"date: ");
        self.0
            .date_service
            .set_date(|date| buf[6..35].copy_from_slice(&date.bytes));
        buf[35..].copy_from_slice(b"\r\n\r\n");
        dst.extend_from_slice(&buf);
    }

    pub(crate) fn set_date_header(&self, dst: &mut BytesMut) {
        self.0
            .date_service
            .set_date(|date| dst.extend_from_slice(&date.bytes));
    }
}

#[derive(Copy, Clone)]
struct Date {
    bytes: [u8; DATE_VALUE_LENGTH],
    pos: usize,
}

impl Date {
    fn new() -> Date {
        let mut date = Date {
            bytes: [0; DATE_VALUE_LENGTH],
            pos: 0,
        };
        date.update();
        date
    }

    fn update(&mut self) {
        self.pos = 0;
        write!(
            self,
            "{}",
            OffsetDateTime::now_utc().format("%a, %d %b %Y %H:%M:%S GMT")
        )
        .unwrap();
    }
}

impl fmt::Write for Date {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let len = s.len();
        self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
        self.pos += len;
        Ok(())
    }
}

/// Service for update Date and Instant periodically at 500 millis interval.
struct DateService {
    current: Rc<Cell<(Date, Instant)>>,
    handle: JoinHandle<()>,
}

impl Drop for DateService {
    fn drop(&mut self) {
        // stop the timer update async task on drop.
        self.handle.abort();
    }
}

impl DateService {
    fn new() -> Self {
        // shared date and timer for DateService and update async task.
        let current = Rc::new(Cell::new((Date::new(), Instant::now())));
        let current_clone = Rc::clone(&current);
        // spawn an async task sleep for 500 milli and update current date/timer in a loop.
        // handle is used to stop the task on DateService drop.
        let handle = actix_rt::spawn(async move {
            #[cfg(test)]
            let _notify = notify_on_drop::NotifyOnDrop::new();

            let mut interval = interval(Duration::from_millis(500));
            loop {
                let now = interval.tick().await;
                let date = Date::new();
                current_clone.set((date, now));
            }
        });

        DateService { current, handle }
    }

    fn now(&self) -> Instant {
        self.current.get().1
    }

    fn set_date<F: FnMut(&Date)>(&self, mut f: F) {
        f(&self.current.get().0);
    }
}

// TODO: move to a util module for testing all spawn handle drop style tasks.
#[cfg(test)]
/// Test Module for checking the drop state of certain async tasks that are spawned
/// with `actix_rt::spawn`
///
/// The target task must explicitly generate `NotifyOnDrop` when spawn the task
mod notify_on_drop {
    use std::cell::RefCell;

    thread_local! {
        static NOTIFY_DROPPED: RefCell<Option<bool>> = RefCell::new(None);
    }

    /// Check if the spawned task is dropped.
    ///
    /// # Panic:
    ///
    /// When there was no `NotifyOnDrop` instance on current thread
    pub(crate) fn is_dropped() -> bool {
        NOTIFY_DROPPED.with(|bool| {
            bool.borrow()
                .expect("No NotifyOnDrop existed on current thread")
        })
    }

    pub(crate) struct NotifyOnDrop;

    impl NotifyOnDrop {
        /// # Panic:
        ///
        /// When construct multiple instances on any given thread.
        pub(crate) fn new() -> Self {
            NOTIFY_DROPPED.with(|bool| {
                let mut bool = bool.borrow_mut();
                if bool.is_some() {
                    panic!("NotifyOnDrop existed on current thread");
                } else {
                    *bool = Some(false);
                }
            });

            NotifyOnDrop
        }
    }

    impl Drop for NotifyOnDrop {
        fn drop(&mut self) {
            NOTIFY_DROPPED.with(|bool| {
                if let Some(b) = bool.borrow_mut().as_mut() {
                    *b = true;
                }
            });
        }
    }
}

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

    use actix_rt::task::yield_now;

    #[actix_rt::test]
    async fn test_date_service_update() {
        let settings = ServiceConfig::new(KeepAlive::Os, 0, 0, false, None);

        yield_now().await;

        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf1);
        let now1 = settings.now();

        sleep_until(Instant::now() + Duration::from_secs(2)).await;
        yield_now().await;

        let now2 = settings.now();
        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf2);

        assert_ne!(now1, now2);

        assert_ne!(buf1, buf2);

        drop(settings);
        assert!(notify_on_drop::is_dropped());
    }

    #[actix_rt::test]
    async fn test_date_service_drop() {
        let service = Rc::new(DateService::new());

        // yield so date service have a chance to register the spawned timer update task.
        yield_now().await;

        let clone1 = service.clone();
        let clone2 = service.clone();
        let clone3 = service.clone();

        drop(clone1);
        assert_eq!(false, notify_on_drop::is_dropped());
        drop(clone2);
        assert_eq!(false, notify_on_drop::is_dropped());
        drop(clone3);
        assert_eq!(false, notify_on_drop::is_dropped());

        drop(service);
        assert!(notify_on_drop::is_dropped());
    }

    #[test]
    fn test_date_len() {
        assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
    }

    #[actix_rt::test]
    async fn test_date() {
        let settings = ServiceConfig::new(KeepAlive::Os, 0, 0, false, None);
        let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf1);
        let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
        settings.set_date(&mut buf2);
        assert_eq!(buf1, buf2);
    }
}
<|endoftext|>


<commit_before>#[macro_export]
#[doc(hidden)]
macro_rules! downcast_get_type_id {
    () => {
        /// A helper method to get the type ID of the type
        /// this trait is implemented on.
        /// This method is unsafe to *implement*, since `downcast_ref` relies
        /// on the returned `TypeId` to perform a cast.
        ///
        /// Unfortunately, Rust has no notion of a trait method that is
        /// unsafe to implement (marking it as `unsafe` makes it unsafe
        /// to *call*). As a workaround, we require this method
        /// to return a private type along with the `TypeId`. This
        /// private type (`PrivateHelper`) has a private constructor,
        /// making it impossible for safe code to construct outside of
        /// this module. This ensures that safe code cannot violate
        /// type-safety by implementing this method.
        #[doc(hidden)]
        fn __private_get_type_id__(&self) -> (std::any::TypeId, PrivateHelper)
        where
            Self: 'static,
        {
            (std::any::TypeId::of::<Self>(), PrivateHelper(()))
        }
    };
}

//Generate implementation for dyn $name
#[doc(hidden)]
#[macro_export]
macro_rules! downcast {
    ($name:ident) => {
        /// A struct with a private constructor, for use with
        /// `__private_get_type_id__`. Its single field is private,
        /// ensuring that it can only be constructed from this module
        #[doc(hidden)]
        pub struct PrivateHelper(());

        impl dyn $name + 'static {
            /// Downcasts generic body to a specific type.
            pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
                if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
                    // SAFETY: external crates cannot override the default
                    // implementation of `__private_get_type_id__`, since
                    // it requires returning a private type. We can therefore
                    // rely on the returned `TypeId`, which ensures that this
                    // case is correct.
                    unsafe { Some(&*(self as *const dyn $name as *const T)) }
                } else {
                    None
                }
            }

            /// Downcasts a generic body to a mutable specific type.
            pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
                if self.__private_get_type_id__().0 == std::any::TypeId::of::<T>() {
                    // SAFETY: external crates cannot override the default
                    // implementation of `__private_get_type_id__`, since
                    // it requires returning a private type. We can therefore
                    // rely on the returned `TypeId`, which ensures that this
                    // case is correct.
                    unsafe {
                        Some(&mut *(self as *const dyn $name as *const T as *mut T))
                    }
                } else {
                    None
                }
            }
        }
    };
}

#[cfg(test)]
mod tests {
    #![allow(clippy::upper_case_acronyms)]

    trait MB {
        downcast_get_type_id!();
    }

    downcast!(MB);

    impl MB for String {}
    impl MB for () {}

    #[actix_rt::test]
    async fn test_any_casting() {
        let mut body = String::from("hello cast");
        let resp_body: &mut dyn MB = &mut body;
        let body = resp_body.downcast_ref::<String>().unwrap();
        assert_eq!(body, "hello cast");
        let body = &mut resp_body.downcast_mut::<String>().unwrap();
        body.push('!');
        let body = resp_body.downcast_ref::<String>().unwrap();
        assert_eq!(body, "hello cast!");
        let not_body = resp_body.downcast_ref::<()>();
        assert!(not_body.is_none());
    }
}
<commit_msg>Fix loophole in soundness of `__private_get_type_id__` (#2199)<commit_after>#[macro_export]
#[doc(hidden)]
macro_rules! downcast_get_type_id {
    () => {
        /// A helper method to get the type ID of the type
        /// this trait is implemented on.
        /// This method is unsafe to *implement*, since `downcast_ref` relies
        /// on the returned `TypeId` to perform a cast.
        ///
        /// Unfortunately, Rust has no notion of a trait method that is
        /// unsafe to implement (marking it as `unsafe` makes it unsafe
        /// to *call*). As a workaround, we require this method
        /// to return a private type along with the `TypeId`. This
        /// private type (`PrivateHelper`) has a private constructor,
        /// making it impossible for safe code to construct outside of
        /// this module. This ensures that safe code cannot violate
        /// type-safety by implementing this method.
        ///
        /// We also take `PrivateHelper` as a parameter, to ensure that
        /// safe code cannot obtain a `PrivateHelper` instance by
        /// delegating to an existing implementation of `__private_get_type_id__`
        #[doc(hidden)]
        fn __private_get_type_id__(
            &self,
            _: PrivateHelper,
        ) -> (std::any::TypeId, PrivateHelper)
        where
            Self: 'static,
        {
            (std::any::TypeId::of::<Self>(), PrivateHelper(()))
        }
    };
}

//Generate implementation for dyn $name
#[doc(hidden)]
#[macro_export]
macro_rules! downcast {
    ($name:ident) => {
        /// A struct with a private constructor, for use with
        /// `__private_get_type_id__`. Its single field is private,
        /// ensuring that it can only be constructed from this module
        #[doc(hidden)]
        pub struct PrivateHelper(());

        impl dyn $name + 'static {
            /// Downcasts generic body to a specific type.
            pub fn downcast_ref<T: $name + 'static>(&self) -> Option<&T> {
                if self.__private_get_type_id__(PrivateHelper(())).0
                    == std::any::TypeId::of::<T>()
                {
                    // SAFETY: external crates cannot override the default
                    // implementation of `__private_get_type_id__`, since
                    // it requires returning a private type. We can therefore
                    // rely on the returned `TypeId`, which ensures that this
                    // case is correct.
                    unsafe { Some(&*(self as *const dyn $name as *const T)) }
                } else {
                    None
                }
            }

            /// Downcasts a generic body to a mutable specific type.
            pub fn downcast_mut<T: $name + 'static>(&mut self) -> Option<&mut T> {
                if self.__private_get_type_id__(PrivateHelper(())).0
                    == std::any::TypeId::of::<T>()
                {
                    // SAFETY: external crates cannot override the default
                    // implementation of `__private_get_type_id__`, since
                    // it requires returning a private type. We can therefore
                    // rely on the returned `TypeId`, which ensures that this
                    // case is correct.
                    unsafe {
                        Some(&mut *(self as *const dyn $name as *const T as *mut T))
                    }
                } else {
                    None
                }
            }
        }
    };
}

#[cfg(test)]
mod tests {
    #![allow(clippy::upper_case_acronyms)]

    trait MB {
        downcast_get_type_id!();
    }

    downcast!(MB);

    impl MB for String {}
    impl MB for () {}

    #[actix_rt::test]
    async fn test_any_casting() {
        let mut body = String::from("hello cast");
        let resp_body: &mut dyn MB = &mut body;
        let body = resp_body.downcast_ref::<String>().unwrap();
        assert_eq!(body, "hello cast");
        let body = &mut resp_body.downcast_mut::<String>().unwrap();
        body.push('!');
        let body = resp_body.downcast_ref::<String>().unwrap();
        assert_eq!(body, "hello cast!");
        let not_body = resp_body.downcast_ref::<()>();
        assert!(not_body.is_none());
    }
}
<|endoftext|>


<commit_before>fn largest(list: &[i32]) -> i32 {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let numbers = vec![1, 3, 2, 5, 0];

    let result = largest(&numbers);

    println!("The largest number is {}", result);
}
<commit_msg>First try to refactor to generics<commit_after>fn largest<T>(list: &[T]) -> T {
    let mut largest = list[0];

    for &item in list.iter() {
        if item > largest {
            largest = item;
        }
    }

    largest
}

fn main() {
    let numbers = vec![1, 3, 2, 5, 0];

    let result = largest(&numbers);

    println!("The largest number is {}", result);
}
<|endoftext|>


<commit_before>use std::io::{fs, UserRWX, File};
use std::io::fs::PathExtensions;
use std::os;

use support::{execs, paths, cargo_dir, ResultTest};
use hamcrest::{assert_that, existing_file, existing_dir, is_not};

use cargo::util::{process, ProcessBuilder};

fn setup() {
}

fn my_process(s: &str) -> ProcessBuilder {
    process(s)
        .cwd(paths::root())
        .env("HOME", Some(paths::home()))
}

fn cargo_process(s: &str) -> ProcessBuilder {
    process(cargo_dir().join("cargo")).arg(s)
        .cwd(paths::root())
        .env("HOME", Some(paths::home()))
}

test!(simple_lib {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo").arg("--no-git"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
    assert_that(&paths::root().join("foo/.gitignore"), is_not(existing_file()));

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
})

test!(simple_bin {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo").arg("--bin"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/main.rs"), existing_file());

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
    assert_that(&paths::root().join(format!("foo/target/foo{}",
                                            os::consts::EXE_SUFFIX)),
                existing_file());
})

test!(simple_git {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
    assert_that(&paths::root().join("foo/.git"), existing_dir());
    assert_that(&paths::root().join("foo/.gitignore"), existing_file());

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
})

test!(simple_travis {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo").arg("--travis"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
    assert_that(&paths::root().join("foo/.travis.yml"), existing_file());

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
})

test!(no_argument {
    assert_that(cargo_process("new"),
                execs().with_status(1)
                       .with_stderr("Invalid arguments.
Usage:
    cargo new [options] <path>
    cargo new -h | --help
"));
})

test!(existing {
    let dst = paths::root().join("foo");
    fs::mkdir(&dst, UserRWX).assert();
    assert_that(cargo_process("new").arg("foo"),
                execs().with_status(101)
                       .with_stderr(format!("Destination `{}` already exists\n",
                                            dst.display())));
})

test!(finds_author_user {
    assert_that(cargo_process("new").arg("foo").env("USER", Some("foo")),
                execs().with_status(0));

    let toml = paths::root().join("foo/Cargo.toml");
    let toml = File::open(&toml).read_to_string().assert();
    assert!(toml.as_slice().contains(r#"authors = ["foo"]"#));
})

test!(finds_author_git {
    my_process("git").args(["config", "--global", "user.name", "bar"])
                     .exec().assert();
    my_process("git").args(["config", "--global", "user.email", "baz"])
                     .exec().assert();
    assert_that(cargo_process("new").arg("foo").env("USER", Some("foo")),
                execs().with_status(0));

    let toml = paths::root().join("foo/Cargo.toml");
    let toml = File::open(&toml).read_to_string().assert();
    assert!(toml.as_slice().contains(r#"authors = ["bar <baz>"]"#));
})

test!(author_prefers_cargo {
    my_process("git").args(["config", "--global", "user.name", "bar"])
                     .exec().assert();
    my_process("git").args(["config", "--global", "user.email", "baz"])
                     .exec().assert();
    let root = paths::root();
    fs::mkdir(&root.join(".cargo"), UserRWX).assert();
    File::create(&root.join(".cargo/config")).write_str(r#"
        [cargo-new]
        name = "new-foo"
        email = "new-bar"
        git = false
    "#).assert();

    assert_that(cargo_process("new").arg("foo").env("USER", Some("foo")),
                execs().with_status(0));

    let toml = paths::root().join("foo/Cargo.toml");
    let toml = File::open(&toml).read_to_string().assert();
    assert!(toml.as_slice().contains(r#"authors = ["new-foo <new-bar>"]"#));
    assert!(!root.join("foo/.gitignore").exists());
})

test!(git_prefers_command_line {
    let root = paths::root();
    fs::mkdir(&root.join(".cargo"), UserRWX).assert();
    File::create(&root.join(".cargo/config")).write_str(r#"
        [cargo-new]
        git = false
        name = "foo"
        email = "bar"
    "#).assert();

    assert_that(cargo_process("new").arg("foo").arg("--git")
                                    .env("USER", Some("foo")),
                execs().with_status(0));
    assert!(root.join("foo/.gitignore").exists());
})
<commit_msg>Make a test more resilient against the host system<commit_after>use std::io::{fs, UserRWX, File, TempDir};
use std::io::fs::PathExtensions;
use std::os;

use support::{execs, paths, cargo_dir, ResultTest};
use hamcrest::{assert_that, existing_file, existing_dir, is_not};

use cargo::util::{process, ProcessBuilder};

fn setup() {
}

fn my_process(s: &str) -> ProcessBuilder {
    process(s)
        .cwd(paths::root())
        .env("HOME", Some(paths::home()))
}

fn cargo_process(s: &str) -> ProcessBuilder {
    process(cargo_dir().join("cargo")).arg(s)
        .cwd(paths::root())
        .env("HOME", Some(paths::home()))
}

test!(simple_lib {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo").arg("--no-git"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
    assert_that(&paths::root().join("foo/.gitignore"), is_not(existing_file()));

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
})

test!(simple_bin {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo").arg("--bin"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/main.rs"), existing_file());

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
    assert_that(&paths::root().join(format!("foo/target/foo{}",
                                            os::consts::EXE_SUFFIX)),
                existing_file());
})

test!(simple_git {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
    assert_that(&paths::root().join("foo/.git"), existing_dir());
    assert_that(&paths::root().join("foo/.gitignore"), existing_file());

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
})

test!(simple_travis {
    os::setenv("USER", "foo");
    assert_that(cargo_process("new").arg("foo").arg("--travis"),
                execs().with_status(0));

    assert_that(&paths::root().join("foo"), existing_dir());
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
    assert_that(&paths::root().join("foo/.travis.yml"), existing_file());

    assert_that(cargo_process("build").cwd(paths::root().join("foo")),
                execs().with_status(0));
})

test!(no_argument {
    assert_that(cargo_process("new"),
                execs().with_status(1)
                       .with_stderr("Invalid arguments.
Usage:
    cargo new [options] <path>
    cargo new -h | --help
"));
})

test!(existing {
    let dst = paths::root().join("foo");
    fs::mkdir(&dst, UserRWX).assert();
    assert_that(cargo_process("new").arg("foo"),
                execs().with_status(101)
                       .with_stderr(format!("Destination `{}` already exists\n",
                                            dst.display())));
})

test!(finds_author_user {
    // Use a temp dir to make sure we don't pick up .cargo/config somewhere in
    // the hierarchy
    let td = TempDir::new("cargo").unwrap();
    assert_that(cargo_process("new").arg("foo").env("USER", Some("foo"))
                                    .cwd(td.path().clone()),
                execs().with_status(0));

    let toml = td.path().join("foo/Cargo.toml");
    let toml = File::open(&toml).read_to_string().assert();
    assert!(toml.as_slice().contains(r#"authors = ["foo"]"#));
})

test!(finds_author_git {
    my_process("git").args(["config", "--global", "user.name", "bar"])
                     .exec().assert();
    my_process("git").args(["config", "--global", "user.email", "baz"])
                     .exec().assert();
    assert_that(cargo_process("new").arg("foo").env("USER", Some("foo")),
                execs().with_status(0));

    let toml = paths::root().join("foo/Cargo.toml");
    let toml = File::open(&toml).read_to_string().assert();
    assert!(toml.as_slice().contains(r#"authors = ["bar <baz>"]"#));
})

test!(author_prefers_cargo {
    my_process("git").args(["config", "--global", "user.name", "bar"])
                     .exec().assert();
    my_process("git").args(["config", "--global", "user.email", "baz"])
                     .exec().assert();
    let root = paths::root();
    fs::mkdir(&root.join(".cargo"), UserRWX).assert();
    File::create(&root.join(".cargo/config")).write_str(r#"
        [cargo-new]
        name = "new-foo"
        email = "new-bar"
        git = false
    "#).assert();

    assert_that(cargo_process("new").arg("foo").env("USER", Some("foo")),
                execs().with_status(0));

    let toml = paths::root().join("foo/Cargo.toml");
    let toml = File::open(&toml).read_to_string().assert();
    assert!(toml.as_slice().contains(r#"authors = ["new-foo <new-bar>"]"#));
    assert!(!root.join("foo/.gitignore").exists());
})

test!(git_prefers_command_line {
    let root = paths::root();
    fs::mkdir(&root.join(".cargo"), UserRWX).assert();
    File::create(&root.join(".cargo/config")).write_str(r#"
        [cargo-new]
        git = false
        name = "foo"
        email = "bar"
    "#).assert();

    assert_that(cargo_process("new").arg("foo").arg("--git")
                                    .env("USER", Some("foo")),
                execs().with_status(0));
    assert!(root.join("foo/.gitignore").exists());
})
<|endoftext|>


<commit_before>extern crate rand;
extern crate djinn;
extern crate redis;
extern crate rustc_serialize;

use rand::Rng;
use redis::Client;
use djinn::{Agent, Manager, Simulation, Population, Updates, Redis, run};
use rand::distributions::{Weighted, WeightedChoice, IndependentSample};
use std::collections::HashMap;

// when two people share the exact same polarity on an opinion,
// this is how much their trust improves by
const MAX_POS_TRUST_SHIFT: f64 = 1.;

// if two people have a polarity within +/- this value,
// their trust improves
const MAX_POS_TRUST_SHIFT_RANGE: f64 = 4.;

// when an opinion shifts, it shifts by this proportion of
// the opinion difference
const OPINION_SHIFT_PROPORTION: f64 = 0.1;

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct Opinion {
    polarity: i32,
    priority: u32,
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct Person {
    opinions: Vec<Opinion>,
    medias: HashMap<u64, u32>,
    friends: HashMap<u64, u32>,
}

impl Person {
    pub fn new(opinions: Vec<Opinion>) -> Person {
        Person {
            opinions: opinions,
            medias: HashMap::new(),
            friends: HashMap::new(),
        }
    }

    /// Select a random opinion weighted by personal priority.
    fn rand_opinion_idx(&self, mut rng: &mut Rng, opinions: &Vec<Opinion>) -> usize {
        let mut items: Vec<Weighted<usize>> = opinions.iter()
            .enumerate()
            .map(|(i, o)| {
                Weighted {
                    item: i,
                    weight: self.opinions[i].priority,
                }
            })
            .collect();
        let wc = WeightedChoice::new(&mut items);
        wc.ind_sample(&mut rng).clone()
    }

    /// Select a random edge by weight.
    fn rand_edge(&self, mut rng: &mut Rng, edges: &HashMap<u64, u32>) -> u64 {
        let mut items: Vec<Weighted<u64>> = edges.iter()
            .map(|(id, weight)| {
                Weighted {
                    item: *id,
                    weight: *weight,
                }
            })
            .collect();
        let wc = WeightedChoice::new(&mut items);
        wc.ind_sample(&mut rng)
    }

    /// Trust is computed from alignment of opinion polarities,
    /// weighted by the importance/priority of those opinions
    /// from the perspective of the subject.
    fn trust_from_opinions(&self, opinions: &Vec<Opinion>) -> u32 {
        self.opinions
            .iter()
            .zip(opinions.iter())
            .fold(0, |acc, (o1, o2)| acc + self.trust_from_opinion(o1, o2))
    }

    fn trust_from_opinion(&self, o1: &Opinion, o2: &Opinion) -> u32 {
        let dist = (o1.polarity - o2.polarity).abs();
        let x = ((2 - dist) as f64) / 1.;
        ((x - 1.) * (o1.priority as f64)).round() as u32
    }

    /// TODO atm only considering a shift _towards_ the other person's opinion,
    /// but totally possible that encountering a divergent opinion
    /// causes one to double-down on their own opinion.
    /// TODO atm this is only a one-way exchange, totally possible to influence the
    /// other person's opinion too.
    fn be_influenced(&self, op_idx: usize, op1: &Opinion, op2: &Opinion) -> PersonUpdate {
        let diff = op2.polarity - op1.polarity;
        let shift = ((diff as f64) * OPINION_SHIFT_PROPORTION).round() as i32;
        PersonUpdate::OpinionShift {
            idx: op_idx,
            polarity: shift,
        }
    }

    /// Based on hearing this person's opinion, adjust trust.
    /// The lower distance between their opinions, the more they trust each other.
    fn trust_shift(&self, op1: &Opinion, op2: &Opinion) -> i32 {
        let dist = (op1.polarity - op2.polarity).abs();
        let trust_shift = -((dist as f64) / MAX_POS_TRUST_SHIFT_RANGE).powi(2) +
                          MAX_POS_TRUST_SHIFT;
        trust_shift.round() as i32
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum EdgeType {
    Media,
    Friend,
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct Media {
    opinions: Vec<Opinion>,
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum State {
    Person(Person),
    Media(Media),
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum PersonUpdate {
    OpinionShift { idx: usize, polarity: i32 },
    TrustShift {
        id: u64,
        shift: i32,
        edge_type: EdgeType,
    },
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum MediaUpdate {
    Click { idx: usize, polarity: i32 },
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum Update {
    Person(PersonUpdate),
    Media(MediaUpdate),
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct World {
    weather: String,
}

#[derive(Clone)]
struct OpinionDynamicsSim;

impl OpinionDynamicsSim {
    fn decide_media<R: Redis>(&self,
                              id: u64,
                              media: &Media,
                              pop: &Population<Self, R>,
                              updates: &mut Updates<Self>)
                              -> () {
        // media's not really doing anything atm
    }

    fn decide_person<R: Redis>(&self,
                               id: u64,
                               person: &Person,
                               pop: &Population<Self, R>,
                               updates: &mut Updates<Self>)
                               -> () {
        let mut rng = rand::weak_rng();

        // talk to a person or consume media?
        let other = if rng.gen::<f64>() < 0.5 {
            // choose a media.
            // the less media this person is familiar with,
            // the more likely they will encounter a random one.
            // otherwise, they choose one with probability based on how much they trust it.
            let p_rand_media = (person.medias.len() as f64) / 2.; // TODO denom should be a config val
            if rng.gen::<f64>() < p_rand_media {
                pop.random("media")
            } else {
                let id = person.rand_edge(&mut rng, &person.medias);
                pop.get_agent(id).unwrap()
            }
        } else {
            // choose a person to talk to.
            let p_rand_person = (person.friends.len() as f64) / 2.; // TODO denom should be a config val
            if rng.gen::<f64>() < p_rand_person {
                pop.random("person") // TODO prob shouldnt be themselves
            } else {
                let id = person.rand_edge(&mut rng, &person.friends);
                pop.get_agent(id).unwrap()
            }
        };
        match other.state {
            State::Person(ref p) => {
                let op_idx = person.rand_opinion_idx(&mut rng, &p.opinions);
                let ref op1 = person.opinions[op_idx];
                let ref op2 = p.opinions[op_idx];

                // naively bootstrap trust for new person as 0
                let trust = match person.friends.get(&other.id) {
                    Some(t) => *t,
                    None => 0,
                };
                let p_opinion_shift = ((trust as f64) + 0.01) / 100.;
                if rng.gen::<f64>() < p_opinion_shift {
                    updates.queue(id, Update::Person(person.be_influenced(op_idx, op1, op2)));
                }

                updates.queue(id,
                              Update::Person(PersonUpdate::TrustShift {
                                  id: other.id,
                                  shift: person.trust_shift(op1, op2),
                                  edge_type: EdgeType::Friend,
                              }))
            }
            State::Media(ref m) => {
                let op_idx = person.rand_opinion_idx(&mut rng, &m.opinions);
                let ref op1 = person.opinions[op_idx];
                let ref op2 = m.opinions[op_idx];

                // "click" on the story
                updates.queue(other.id,
                              Update::Media(MediaUpdate::Click {
                                  idx: op_idx,
                                  polarity: op1.polarity,
                              }));

                // naively bootstrap trust for new media as 0
                let trust = match person.medias.get(&other.id) {
                    Some(t) => *t,
                    None => 0,
                };

                let p_opinion_shift = ((trust as f64) + 0.01) / 100.;
                if rng.gen::<f64>() < p_opinion_shift {
                    updates.queue(id, Update::Person(person.be_influenced(op_idx, op1, op2)));
                }

                updates.queue(id,
                              Update::Person(PersonUpdate::TrustShift {
                                  id: other.id,
                                  shift: person.trust_shift(op1, op2),
                                  edge_type: EdgeType::Media,
                              }))
            }
        }
    }

    fn update_person(&self, mut person: &mut Person, updates: Vec<Update>) -> bool {
        let mut updated = false;
        for update in updates {
            match update {
                Update::Person(u) => {
                    match u {
                        PersonUpdate::OpinionShift { idx, polarity } => {
                            let ref mut op = person.opinions[idx];
                            op.polarity += polarity;
                            updated = true;
                        }
                        PersonUpdate::TrustShift { id, shift, edge_type } => {
                            match edge_type {
                                EdgeType::Friend => {
                                    let trust = *person.friends.entry(id).or_insert(0) as i32;
                                    person.friends.insert(id, (trust + shift) as u32);
                                }
                                EdgeType::Media => {
                                    let trust = *person.medias.entry(id).or_insert(0) as i32;
                                    person.medias.insert(id, (trust + shift) as u32);
                                }
                            }
                            updated = true;
                        }
                    }
                }
                _ => (),
            }
        }
        updated
    }

    fn update_media(&self, mut media: &mut Media, updates: Vec<Update>) -> bool {
        let mut updated = false;
        for update in updates {
            match update {
                Update::Media(u) => {
                    match u {
                        MediaUpdate::Click { idx, polarity } => {
                            let ref mut op = media.opinions[idx];
                            let diff = polarity - op.polarity;
                            op.polarity += ((polarity as f64) * OPINION_SHIFT_PROPORTION)
                                .round() as i32;
                            op.priority += 1;
                            updated = true;
                        }
                    }
                }
                _ => (),
            }
        }
        updated
    }
}

impl Simulation for OpinionDynamicsSim {
    type State = State;
    type Update = Update;
    type World = World;

    fn decide<R: Redis>(&self,
                        agent: &Agent<Self::State>,
                        world: &Self::World,
                        pop: &Population<Self, R>,
                        updates: &mut Updates<Self>)
                        -> () {
        match agent.state {
            State::Person(ref p) => {
                self.decide_person(agent.id, p, pop, updates);
            }
            State::Media(ref m) => {
                self.decide_media(agent.id, m, pop, updates);
            }
        }
    }

    fn update(&self, mut state: &mut Self::State, updates: Vec<Self::Update>) -> bool {
        match *state {
            State::Person(ref mut p) => self.update_person(p, updates),
            State::Media(ref mut m) => self.update_media(m, updates),
        }
    }

    fn on_spawns<R: Redis>(&self,
                           agents: Vec<Agent<Self::State>>,
                           population: &Population<Self, R>)
                           -> () {
        // index newly created media/people
        // so we can sample them later
        let mut media = Vec::new();
        let mut people = Vec::new();
        let _: Vec<()> = agents.iter()
            .map(|a| {
                match a.state {
                    State::Person(_) => people.push(a.id),
                    State::Media(_) => media.push(a.id),
                }
            })
            .collect();
        let _: () = population.indexes("media", media);
        let _: () = population.indexes("people", people);
    }
}

// TODO
// person step:
//  - sample a person to talk to, based on trust. or encounter random.
//  - read a story from media, based on opinion alignment and importance
//  - talk to person
//  - change opinion based on media
//  - change opinion based on talking to person
//

fn main() {
    // TODO
    // - bootstrap social network
    // - people more likely talk to likeminded friends
    // - randomly meet new people

    let sim = OpinionDynamicsSim {};

    // TODO
    let world = World { weather: "sunny".to_string() };

    // Setup the manager
    let addr = "redis://127.0.0.1/";
    let pop_client = Client::open(addr).unwrap();
    let mut manager = Manager::new(addr, pop_client, sim.clone());

    let mut medias = vec![Media {
                              opinions: vec![Opinion {
                                                 polarity: -1,
                                                 priority: 5,
                                             },
                                             Opinion {
                                                 polarity: -5,
                                                 priority: 1,
                                             }],
                          },
                          Media {
                              opinions: vec![Opinion {
                                                 polarity: 1,
                                                 priority: 8,
                                             },
                                             Opinion {
                                                 polarity: 2,
                                                 priority: 4,
                                             }],
                          }];
    let media_ids = manager.spawns(medias.drain(..).map(|m| State::Media(m)).collect());

    let mut people = vec![Person::new(vec![Opinion {
                                               polarity: 1,
                                               priority: 1,
                                           },
                                           Opinion {
                                               polarity: 0,
                                               priority: 0,
                                           }]),
                          Person::new(vec![Opinion {
                                               polarity: -1,
                                               priority: 1,
                                           },
                                           Opinion {
                                               polarity: 1,
                                               priority: 0,
                                           }])];
    let people_ids = manager.spawns(people.drain(..).map(|m| State::Person(m)).collect());

    // run(sim, world, manager, 4, 10);
}
<commit_msg>some fixes to the opdyn model<commit_after>extern crate rand;
extern crate djinn;
extern crate redis;
extern crate rustc_serialize;

use rand::Rng;
use redis::Client;
use djinn::{Agent, Manager, Simulation, Population, Updates, Redis, run};
use rand::distributions::{Weighted, WeightedChoice, IndependentSample};
use std::collections::HashMap;

// when two people share the exact same polarity on an opinion,
// this is how much their trust improves by
const MAX_POS_TRUST_SHIFT: f64 = 1.;

// if two people have a polarity within +/- this value,
// their trust improves
const MAX_POS_TRUST_SHIFT_RANGE: f64 = 4.;

// when an opinion shifts, it shifts by this proportion of
// the opinion difference
const OPINION_SHIFT_PROPORTION: f64 = 0.1;

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct Opinion {
    polarity: i32,
    priority: u32,
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct Person {
    opinions: Vec<Opinion>,
    medias: HashMap<u64, u32>,
    friends: HashMap<u64, u32>,
}

impl Person {
    pub fn new(opinions: Vec<Opinion>) -> Person {
        Person {
            opinions: opinions,
            medias: HashMap::new(),
            friends: HashMap::new(),
        }
    }

    /// Select a random opinion weighted by personal priority.
    fn rand_opinion_idx(&self, mut rng: &mut Rng, opinions: &Vec<Opinion>) -> usize {
        let mut items: Vec<Weighted<usize>> = opinions.iter()
            .enumerate()
            .map(|(i, o)| {
                Weighted {
                    item: i,
                    weight: self.opinions[i].priority,
                }
            })
            .collect();
        let wc = WeightedChoice::new(&mut items);
        wc.ind_sample(&mut rng).clone()
    }

    /// Select a random edge by weight.
    fn rand_edge(&self, mut rng: &mut Rng, edges: &HashMap<u64, u32>) -> u64 {
        let mut items: Vec<Weighted<u64>> = edges.iter()
            .map(|(id, weight)| {
                Weighted {
                    item: *id,
                    weight: *weight,
                }
            })
            .collect();
        let wc = WeightedChoice::new(&mut items);
        wc.ind_sample(&mut rng)
    }

    /// Trust is computed from alignment of opinion polarities,
    /// weighted by the importance/priority of those opinions
    /// from the perspective of the subject.
    fn trust_from_opinions(&self, opinions: &Vec<Opinion>) -> u32 {
        self.opinions
            .iter()
            .zip(opinions.iter())
            .fold(0, |acc, (o1, o2)| acc + self.trust_from_opinion(o1, o2))
    }

    fn trust_from_opinion(&self, o1: &Opinion, o2: &Opinion) -> u32 {
        let dist = (o1.polarity - o2.polarity).abs();
        let x = ((2 - dist) as f64) / 1.;
        ((x - 1.) * (o1.priority as f64)).round() as u32
    }

    /// TODO atm only considering a shift _towards_ the other person's opinion,
    /// but totally possible that encountering a divergent opinion
    /// causes one to double-down on their own opinion.
    /// TODO atm this is only a one-way exchange, totally possible to influence the
    /// other person's opinion too.
    fn be_influenced(&self, op_idx: usize, op1: &Opinion, op2: &Opinion) -> PersonUpdate {
        let diff = op2.polarity - op1.polarity;
        let shift = ((diff as f64) * OPINION_SHIFT_PROPORTION).round() as i32;
        PersonUpdate::OpinionShift {
            idx: op_idx,
            polarity: shift,
        }
    }

    /// Based on hearing this person's opinion, adjust trust.
    /// The lower distance between their opinions, the more they trust each other.
    fn trust_shift(&self, op1: &Opinion, op2: &Opinion) -> i32 {
        let dist = (op1.polarity - op2.polarity).abs();
        let trust_shift = -((dist as f64) / MAX_POS_TRUST_SHIFT_RANGE).powi(2) +
                          MAX_POS_TRUST_SHIFT;
        trust_shift.round() as i32
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum EdgeType {
    Media,
    Friend,
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct Media {
    opinions: Vec<Opinion>,
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum State {
    Person(Person),
    Media(Media),
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum PersonUpdate {
    OpinionShift { idx: usize, polarity: i32 },
    TrustShift {
        id: u64,
        shift: i32,
        edge_type: EdgeType,
    },
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum MediaUpdate {
    Click { idx: usize, polarity: i32 },
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
enum Update {
    Person(PersonUpdate),
    Media(MediaUpdate),
}

#[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)]
struct World {}

#[derive(Clone)]
struct OpinionDynamicsSim;

impl OpinionDynamicsSim {
    fn decide_media<R: Redis>(&self,
                              id: u64,
                              media: &Media,
                              pop: &Population<Self, R>,
                              updates: &mut Updates<Self>)
                              -> () {
        // media's not really doing anything atm
    }

    fn decide_person<R: Redis>(&self,
                               id: u64,
                               person: &Person,
                               pop: &Population<Self, R>,
                               updates: &mut Updates<Self>)
                               -> () {
        let mut rng = rand::weak_rng();

        // talk to a person or consume media?
        let other = if rng.gen::<f64>() < 0.5 {
            // choose a media.
            // the less media this person is familiar with,
            // the more likely they will encounter a random one.
            // otherwise, they choose one with probability based on how much they trust it.
            let p_rand_media = 1. - ((person.medias.len() as f64) / 2.); // TODO denom should be a config val
            if rng.gen::<f64>() < p_rand_media {
                pop.random("media")
            } else {
                let id = person.rand_edge(&mut rng, &person.medias);
                pop.get_agent(id).unwrap()
            }
        } else {
            // choose a person to talk to.
            let p_rand_person = 1. - ((person.friends.len() as f64) / 2.); // TODO denom should be a config val
            if rng.gen::<f64>() < p_rand_person {
                pop.random("people") // TODO prob shouldnt be themselves
            } else {
                let id = person.rand_edge(&mut rng, &person.friends);
                pop.get_agent(id).unwrap()
            }
        };
        match other.state {
            State::Person(ref p) => {
                let op_idx = person.rand_opinion_idx(&mut rng, &p.opinions);
                let ref op1 = person.opinions[op_idx];
                let ref op2 = p.opinions[op_idx];

                // naively bootstrap trust for new person as 0
                let trust = match person.friends.get(&other.id) {
                    Some(t) => *t,
                    None => 0,
                };
                let p_opinion_shift = ((trust as f64) + 0.01) / 100.;
                if rng.gen::<f64>() < p_opinion_shift {
                    updates.queue(id, Update::Person(person.be_influenced(op_idx, op1, op2)));
                }

                updates.queue(id,
                              Update::Person(PersonUpdate::TrustShift {
                                  id: other.id,
                                  shift: person.trust_shift(op1, op2),
                                  edge_type: EdgeType::Friend,
                              }))
            }
            State::Media(ref m) => {
                let op_idx = person.rand_opinion_idx(&mut rng, &m.opinions);
                let ref op1 = person.opinions[op_idx];
                let ref op2 = m.opinions[op_idx];

                // "click" on the story
                updates.queue(other.id,
                              Update::Media(MediaUpdate::Click {
                                  idx: op_idx,
                                  polarity: op1.polarity,
                              }));

                // naively bootstrap trust for new media as 0
                let trust = match person.medias.get(&other.id) {
                    Some(t) => *t,
                    None => 0,
                };

                let p_opinion_shift = ((trust as f64) + 0.01) / 100.;
                if rng.gen::<f64>() < p_opinion_shift {
                    updates.queue(id, Update::Person(person.be_influenced(op_idx, op1, op2)));
                }

                updates.queue(id,
                              Update::Person(PersonUpdate::TrustShift {
                                  id: other.id,
                                  shift: person.trust_shift(op1, op2),
                                  edge_type: EdgeType::Media,
                              }))
            }
        }
    }

    fn update_person(&self, mut person: &mut Person, updates: Vec<Update>) -> bool {
        let mut updated = false;
        for update in updates {
            match update {
                Update::Person(u) => {
                    match u {
                        PersonUpdate::OpinionShift { idx, polarity } => {
                            let ref mut op = person.opinions[idx];
                            op.polarity += polarity;
                            updated = true;
                        }
                        PersonUpdate::TrustShift { id, shift, edge_type } => {
                            match edge_type {
                                EdgeType::Friend => {
                                    let trust = *person.friends.entry(id).or_insert(0) as i32;
                                    person.friends.insert(id, (trust + shift) as u32);
                                }
                                EdgeType::Media => {
                                    let trust = *person.medias.entry(id).or_insert(0) as i32;
                                    person.medias.insert(id, (trust + shift) as u32);
                                }
                            }
                            updated = true;
                        }
                    }
                }
                _ => (),
            }
        }
        updated
    }

    fn update_media(&self, mut media: &mut Media, updates: Vec<Update>) -> bool {
        let mut updated = false;
        for update in updates {
            match update {
                Update::Media(u) => {
                    match u {
                        MediaUpdate::Click { idx, polarity } => {
                            let ref mut op = media.opinions[idx];
                            let diff = polarity - op.polarity;
                            op.polarity += ((polarity as f64) * OPINION_SHIFT_PROPORTION)
                                .round() as i32;
                            op.priority += 1;
                            updated = true;
                        }
                    }
                }
                _ => (),
            }
        }
        updated
    }
}

impl Simulation for OpinionDynamicsSim {
    type State = State;
    type Update = Update;
    type World = World;

    fn decide<R: Redis>(&self,
                        agent: &Agent<Self::State>,
                        world: &Self::World,
                        pop: &Population<Self, R>,
                        updates: &mut Updates<Self>)
                        -> () {
        match agent.state {
            State::Person(ref p) => {
                self.decide_person(agent.id, p, pop, updates);
            }
            State::Media(ref m) => {
                self.decide_media(agent.id, m, pop, updates);
            }
        }
    }

    fn update(&self, mut state: &mut Self::State, updates: Vec<Self::Update>) -> bool {
        match *state {
            State::Person(ref mut p) => self.update_person(p, updates),
            State::Media(ref mut m) => self.update_media(m, updates),
        }
    }

    fn on_spawns<R: Redis>(&self,
                           agents: Vec<Agent<Self::State>>,
                           population: &Population<Self, R>)
                           -> () {
        // index newly created media/people
        // so we can sample them later
        let mut media = Vec::new();
        let mut people = Vec::new();
        let _: Vec<()> = agents.iter()
            .map(|a| {
                match a.state {
                    State::Person(_) => people.push(a.id),
                    State::Media(_) => media.push(a.id),
                }
            })
            .collect();
        let _: () = population.indexes("media", media);
        let _: () = population.indexes("people", people);
    }
}

fn main() {
    let sim = OpinionDynamicsSim {};
    let world = World {};

    // Setup the manager
    let addr = "redis://127.0.0.1/";
    let pop_client = Client::open(addr).unwrap();
    let mut manager = Manager::new(addr, pop_client, sim.clone());

    let mut medias = vec![Media {
                              opinions: vec![Opinion {
                                                 polarity: -1,
                                                 priority: 5,
                                             },
                                             Opinion {
                                                 polarity: -5,
                                                 priority: 1,
                                             }],
                          },
                          Media {
                              opinions: vec![Opinion {
                                                 polarity: 1,
                                                 priority: 8,
                                             },
                                             Opinion {
                                                 polarity: 2,
                                                 priority: 4,
                                             }],
                          }];
    let media_ids = manager.spawns(medias.drain(..).map(|m| State::Media(m)).collect());

    let mut people = vec![Person::new(vec![Opinion {
                                               polarity: 1,
                                               priority: 1,
                                           },
                                           Opinion {
                                               polarity: 0,
                                               priority: 0,
                                           }]),
                          Person::new(vec![Opinion {
                                               polarity: -1,
                                               priority: 1,
                                           },
                                           Opinion {
                                               polarity: 1,
                                               priority: 0,
                                           }])];
    let people_ids = manager.spawns(people.drain(..).map(|m| State::Person(m)).collect());

    println!("running");
    run(sim, world, manager, 4, 10);
}
<|endoftext|>