<commit_before>use hyper::{Uri, Client, Method, Error};
use hyper::header::UserAgent;
use hyper::client::{Request, FutureResponse};
use hyper_tls::HttpsConnector;
use futures::{Future, Stream};
use futures::future;
use serde_json;
use serde::Deserialize;

use std::fs::{File, OpenOptions}; // TODO file writing to utils.rs
use std::io::Write;
use std::path::Path;

use curl::easy::Easy;
use logging_utils::{log_response_status, log_written_file};
use utils::parse_url_from_str;
use utils;
use super::models::*;
use super::endpoint::HnNewsEndpoint;
use super::app::{AppDomain, AppStates, AppStateMachine};

pub fn get_top_story_ids(app_domain: &mut AppDomain,
                         state: &mut AppStateMachine)
                         -> Result<HnListOfItems, Error> {
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_top_story_ids(&client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger,
                                &endpoint.get_top_stories_path(),
                                &res.status().to_string());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnListOfItems>(chunks));
    let result = app_domain.core.run(work);
    state.current_state = AppStates::DoingLocalWork;
    result
}

fn deserialize<T: Deserialize>(chunks: Vec<u8>) -> T {
    let s = String::from_utf8(chunks).unwrap();
    let deserialized: T = serde_json::from_str(&s).unwrap();
    deserialized
}

///
/// Gets HnItem wrapped in Result
///
pub fn get_item_by_id(item: &str,
                      app_domain: &mut AppDomain,
                      state: &mut AppStateMachine)
                      -> Result<HnItem, Error> {
    // note kids in item are comments, parts not sure what it is
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_item(&item, &client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger,
                                &endpoint.get_item_path(&item),
                                &res.status().to_string());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnItem>(chunks));
    state.current_state = AppStates::DoingLocalWork;
    let result = app_domain.core.run(work);
    result
}

pub fn get_comments_for_item(item: &HnItem,
                             app_domain: &mut AppDomain,
                             state: &mut AppStateMachine)
                             -> Option<Vec<HnItem>> {
    match item.kids {
        Some(ref kids) => {
            let core = &mut app_domain.core;
            let logger = &mut app_domain.logger; // These need to be mutable as otherwise it'll cause mutable<>immutable borrow error
            let endpoint = &mut app_domain.endpoint;
            let client = &mut app_domain.client;
            state.current_state = AppStates::RetrievingResults;

            let comments: &Vec<i32> = &kids;
            info!(&logger,
                  format!("Retrieving comments for {} with {} comments",
                          &item.id,
                          kids.len()));
            let items = comments.iter()
                .map(|item_id| {
                    (item_id.to_string(), request_item(&item_id.to_string(), &client, &endpoint))
                })
                .map(|(_, request_work)| {
                    let subtask = request_work.and_then(|response| {
                        response.body()
                            .fold(Vec::new(), |mut v, chunk| {
                                v.extend(&chunk[..]);
                                future::ok::<_, Error>(v)
                            })
                    });
                    core.run(subtask).unwrap()
                })
                .collect::<Vec<Vec<u8>>>()
                .into_iter()
                .map(|chunks| deserialize::<HnItem>(chunks))
                .filter(|item:&HnItem| item.text.is_some()) // for some reason there are comments which have no text
                .collect::<Vec<HnItem>>();
            state.current_state = AppStates::DoingLocalWork;
            Some(items)
        }
        None => None,
    }
}

fn request_top_story_ids(client: &Client<HttpsConnector>,
                         endpoints: &HnNewsEndpoint)
                         -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_top_stories_path());
    create_get_request(url, &client)
}

fn request_best_stories_ids(client: &Client<HttpsConnector>,
                            endpoints: &HnNewsEndpoint)
                            -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_best_stories_path());
    create_get_request(url, &client)
}

fn request_item(item: &str,
                client: &Client<HttpsConnector>,
                endpoints: &HnNewsEndpoint)
                -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_item_path(item));
    create_get_request(url, &client)
}

pub fn download_page_from_item(item: &HnItem,
                               app_domain: &mut AppDomain,
                               state: &mut AppStateMachine)
                               -> Result<String, String> {
    match item.url {
        // todo change to async
        Some(ref url) => {
            let filename: String = utils::filename_for_hnitem(&item);
            let path = Path::new(&filename);
            let mut file: File =
                OpenOptions::new().write(true).create(true).open(path.as_os_str()).unwrap();

            state.current_state = AppStates::RetrievingResults;
            let page_content = curl_req(url);

            state.current_state = AppStates::DoingLocalWork;
            let page_as_string = String::from_utf8(page_content).unwrap(); // TODO errors

            let write_result = file.write_all(page_as_string.as_bytes());
            log_written_file(&app_domain.logger, write_result.is_ok(), &filename);

            Ok(String::from(path.as_os_str().to_str().unwrap())) // should probably be path to string
            // todo error if could not create
        }
        None => Err(String::from("No url")),
    }

}

fn curl_req(url: &String) -> Vec<u8> {
    let mut vecced: Vec<u8> = Vec::new();
    let mut easy = Easy::new();
    easy.get(true).unwrap();
    easy.url(url).unwrap();
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
                vecced.extend_from_slice(data);
                Ok(data.len())
            })
            .unwrap();
        transfer.perform().unwrap();
    }
    vecced
}

fn create_get_request(url: Uri, client: &Client<HttpsConnector>) -> FutureResponse {
    let mut request = Request::new(Method::Get, url);
    common_headers(&mut request);
    client.request(request)
}

fn common_headers(req: &mut Request) {
    req.headers_mut().set(UserAgent::new("rs-hackernews-cli"));
}

#[cfg(test)]
mod tests {
    use hyper::StatusCode;

    use super::*;
    use app::*;

    #[test]
    fn request_item_test() {
        let mut app_domain = AppDomain::new();
        let s = String::from("8000");
        let response = app_domain.core
            .run(request_item(&s, &app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_top_story_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_best_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_best_stories_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }

    #[test]
    fn get_item_by_id_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("126809");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        assert!(hnitem.score.unwrap() != 0);
        assert!(hnitem.id != 0);
        assert!(!hnitem.type_str.is_empty());
        assert!(!hnitem.title.unwrap().is_empty());
        assert_eq!(126809, hnitem.id);
    }
    #[test]
    fn get_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let top_stories: HnListOfItems = get_top_story_ids(&mut app_domain, &mut app_sm).unwrap();
        assert!(top_stories.values.len() != 0);
    }

    #[test]
    fn get_comments_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("14114235");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        let comments: Vec<HnItem> = get_comments_for_item(&hnitem, &mut app_domain, &mut app_sm)
            .unwrap();
        assert!(comments.len() != 0);
    }
}
<commit_msg>Fixes the test<commit_after>use hyper::{Uri, Client, Method, Error};
use hyper::header::UserAgent;
use hyper::client::{Request, FutureResponse};
use hyper_tls::HttpsConnector;
use futures::{Future, Stream};
use futures::future;
use serde_json;
use serde::Deserialize;

use std::fs::{File, OpenOptions}; // TODO file writing to utils.rs
use std::io::Write;
use std::path::Path;

use curl::easy::Easy;
use logging_utils::{log_response_status, log_written_file};
use utils::parse_url_from_str;
use utils;
use super::models::*;
use super::endpoint::HnNewsEndpoint;
use super::app::{AppDomain, AppStates, AppStateMachine};

pub fn get_top_story_ids(app_domain: &mut AppDomain,
                         state: &mut AppStateMachine)
                         -> Result<HnListOfItems, Error> {
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_top_story_ids(&client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger,
                                &endpoint.get_top_stories_path(),
                                &res.status().to_string());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnListOfItems>(chunks));
    let result = app_domain.core.run(work);
    state.current_state = AppStates::DoingLocalWork;
    result
}

fn deserialize<T: Deserialize>(chunks: Vec<u8>) -> T {
    let s = String::from_utf8(chunks).unwrap();
    let deserialized: T = serde_json::from_str(&s).unwrap();
    deserialized
}

///
/// Gets HnItem wrapped in Result
///
pub fn get_item_by_id(item: &str,
                      app_domain: &mut AppDomain,
                      state: &mut AppStateMachine)
                      -> Result<HnItem, Error> {
    // note kids in item are comments, parts not sure what it is
    let logger = &app_domain.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &app_domain.endpoint;
    let client = &app_domain.client;
    state.current_state = AppStates::RetrievingResults;
    let work = request_item(&item, &client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger,
                                &endpoint.get_item_path(&item),
                                &res.status().to_string());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| deserialize::<HnItem>(chunks));
    state.current_state = AppStates::DoingLocalWork;
    let result = app_domain.core.run(work);
    result
}

pub fn get_comments_for_item(item: &HnItem,
                             app_domain: &mut AppDomain,
                             state: &mut AppStateMachine)
                             -> Option<Vec<HnItem>> {
    match item.kids {
        Some(ref kids) => {
            let core = &mut app_domain.core;
            let logger = &mut app_domain.logger; // These need to be mutable as otherwise it'll cause mutable<>immutable borrow error
            let endpoint = &mut app_domain.endpoint;
            let client = &mut app_domain.client;
            state.current_state = AppStates::RetrievingResults;

            let comments: &Vec<i32> = &kids;
            info!(&logger,
                  format!("Retrieving comments for {} with {} comments",
                          &item.id,
                          kids.len()));
            let items = comments.iter()
                .map(|item_id| {
                    (item_id.to_string(), request_item(&item_id.to_string(), &client, &endpoint))
                })
                .map(|(_, request_work)| {
                    let subtask = request_work.and_then(|response| {
                        response.body()
                            .fold(Vec::new(), |mut v, chunk| {
                                v.extend(&chunk[..]);
                                future::ok::<_, Error>(v)
                            })
                    });
                    core.run(subtask).unwrap()
                })
                .collect::<Vec<Vec<u8>>>()
                .into_iter()
                .map(|chunks| deserialize::<HnItem>(chunks))
                .filter(|item:&HnItem| item.text.is_some()) // for some reason there are comments which have no text
                .collect::<Vec<HnItem>>();
            state.current_state = AppStates::DoingLocalWork;
            Some(items)
        }
        None => None,
    }
}

fn request_top_story_ids(client: &Client<HttpsConnector>,
                         endpoints: &HnNewsEndpoint)
                         -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_top_stories_path());
    create_get_request(url, &client)
}

fn request_best_stories_ids(client: &Client<HttpsConnector>,
                            endpoints: &HnNewsEndpoint)
                            -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_best_stories_path());
    create_get_request(url, &client)
}

fn request_item(item: &str,
                client: &Client<HttpsConnector>,
                endpoints: &HnNewsEndpoint)
                -> FutureResponse {
    let url = parse_url_from_str(&endpoints.get_item_path(item));
    create_get_request(url, &client)
}

pub fn download_page_from_item(item: &HnItem,
                               app_domain: &mut AppDomain,
                               state: &mut AppStateMachine)
                               -> Result<String, String> {
    match item.url {
        // todo change to async
        Some(ref url) => {
            let filename: String = utils::filename_for_hnitem(&item);
            let path = Path::new(&filename);
            let mut file: File =
                OpenOptions::new().write(true).create(true).open(path.as_os_str()).unwrap();

            state.current_state = AppStates::RetrievingResults;
            let page_content = curl_req(url);

            state.current_state = AppStates::DoingLocalWork;
            let page_as_string = String::from_utf8(page_content).unwrap(); // TODO errors

            let write_result = file.write_all(page_as_string.as_bytes());
            log_written_file(&app_domain.logger, write_result.is_ok(), &filename);

            Ok(String::from(path.as_os_str().to_str().unwrap())) // should probably be path to string
            // todo error if could not create
        }
        None => Err(String::from("No url")),
    }

}

fn curl_req(url: &String) -> Vec<u8> {
    let mut vecced: Vec<u8> = Vec::new();
    let mut easy = Easy::new();
    easy.get(true).unwrap();
    easy.url(url).unwrap();
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
                vecced.extend_from_slice(data);
                Ok(data.len())
            })
            .unwrap();
        transfer.perform().unwrap();
    }
    vecced
}

fn create_get_request(url: Uri, client: &Client<HttpsConnector>) -> FutureResponse {
    let mut request = Request::new(Method::Get, url);
    common_headers(&mut request);
    client.request(request)
}

fn common_headers(req: &mut Request) {
    req.headers_mut().set(UserAgent::new("rs-hackernews-cli"));
}

#[cfg(test)]
mod tests {
    use hyper::StatusCode;

    use super::*;
    use core::app::*;

    #[test]
    fn request_item_test() {
        let mut app_domain = AppDomain::new();
        let s = String::from("8000");
        let response = app_domain.core
            .run(request_item(&s, &app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_top_story_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }
    #[test]
    fn request_best_stories_test() {
        let mut app_domain = AppDomain::new();
        let response = app_domain.core
            .run(request_best_stories_ids(&app_domain.client, &app_domain.endpoint))
            .unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }

    #[test]
    fn get_item_by_id_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("126809");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        assert!(hnitem.score.unwrap() != 0);
        assert!(hnitem.id != 0);
        assert!(!hnitem.type_str.is_empty());
        assert!(!hnitem.title.unwrap().is_empty());
        assert_eq!(126809, hnitem.id);
    }
    #[test]
    fn get_top_stories_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let top_stories: HnListOfItems = get_top_story_ids(&mut app_domain, &mut app_sm).unwrap();
        assert!(top_stories.values.len() != 0);
    }

    #[test]
    fn get_comments_test() {
        let mut app_domain = AppDomain::new();
        let mut app_sm = AppStateMachine::new();
        let s = String::from("14114235");
        let hnitem: HnItem = get_item_by_id(&s, &mut app_domain, &mut app_sm).unwrap();
        let comments: Vec<HnItem> = get_comments_for_item(&hnitem, &mut app_domain, &mut app_sm)
            .unwrap();
        assert!(comments.len() != 0);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Servo Project Developers.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.

extern crate fontsan;

use std::io::{Read, self};
use std::{env, process};

fn main() {
    let mut input = vec![];
    io::stdin().read_to_end(&mut input).unwrap();

    match fontsan::process(&input) {
        Ok(output) => println!("ok, result is {} bytes", output.len()),
        Err(_) => {
            println!("there's something fishy about this font!");
            process::exit(1);
        }
    }
}
<commit_msg>Auto merge of #17 - khaledhosny:unused-import, r=nox<commit_after>// Copyright 2015 The Servo Project Developers.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file.

extern crate fontsan;

use std::io::{Read, self};
use std::process;

fn main() {
    let mut input = vec![];
    io::stdin().read_to_end(&mut input).unwrap();

    match fontsan::process(&input) {
        Ok(output) => println!("ok, result is {} bytes", output.len()),
        Err(_) => {
            println!("there's something fishy about this font!");
            process::exit(1);
        }
    }
}
<|endoftext|>


<commit_before>// Claxon -- A FLAC decoding library in Rust
// Copyright 2017 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

// This file contains a minimal example of using Claxon with mp4parse and Hound
// to decode a flac stream inside an MP4 (.mp4 or .m4a) container to a wav file.

extern crate claxon;
extern crate hound;
extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::Seek;
use std::io;
use std::path::Path;

use claxon::metadata::read_metadata_block;
use claxon::metadata::StreamInfo;
use hound::{WavSpec, WavWriter};
use mp4parse::CodecType;

fn decode_file(fname: &Path) {
    // Create a file to read the mp4 structure from. We will later need to seek
    // in this file to get to the parts that contain FLAC data.
    let file = File::open(fname).expect("failed to open mp4 file");
    let mut bufread = io::BufReader::new(file);

    // Parse the mp4 metadata.
    let mut context = mp4parse::MediaContext::new();
    mp4parse::read_mp4(&mut bufread, &mut context).expect("failed to decode mp4");

    // An MP4 file contains one or more tracks. A track can contain a single
    // FLAC stream. Iterate over those.
    for track in &context.tracks {
        if track.codec_type != CodecType::FLAC {
            continue
        }

        let streaminfo = get_streaminfo(track).expect("missing streaminfo");

        // Build a wav writer to write the decoded track to a wav file.
        let spec = WavSpec {
            channels: streaminfo.channels as u16,
            sample_rate: streaminfo.sample_rate,
            bits_per_sample: streaminfo.bits_per_sample as u16,
            sample_format: hound::SampleFormat::Int,
        };

        let fname_wav = fname.with_extension("wav");
        let opt_wav_writer = WavWriter::create(fname_wav, spec);
        let mut wav_writer = opt_wav_writer.expect("failed to create wav file");

        // The data in an MP4 file is split into "chunks", for which we can get
        // the file offset where the chunk starts. Every chunk contains one or
        // more "samples", which is one FLAC frame. These frames are all stored
        // adjacently in the chunk, so we can read them with a fingle frame
        // reader.

        // The "stco" in mp4parse's Track stands for "Chunk Offset Box", "stsc"
        // stands for "Sample to Chunk Box", which actually tells per chunk how
        // many samples it contains, not per sample in which chunk it is.
        let chunk_offsets = &track.stco.as_ref().expect("missing chunk offset box").offsets;
        let chunk_samples = &track.stsc.as_ref().expect("missing sample to chunk box").samples;

        let mut ck_off_iter = chunk_offsets.iter().enumerate();

        for cs in chunk_samples {
            // The "sample" contains the index of the chunk that contains the
            // sample and subsequent samples. Locate the file offset of that
            // chunk in the "Chunk Offset Box", then seek to there. The
            // first_chunk field is a 1-based chunk index, not 0-based.
            let i_off = ck_off_iter.find(|&(i, _off)| 1 + i as u32 == cs.first_chunk);
            let seek_to = i_off.expect("failed to locate chunk offset for mp4 sample").1;
            bufread.seek(io::SeekFrom::Start(*seek_to)).expect("failed to seek to chunk");

            // Decode all the "samples" (FLAC frames) in the chunk.
            bufread = decode_frames(bufread, &streaminfo, cs.samples_per_chunk, &mut wav_writer);
        }

        // Stop iterating over tracks; if there are more FLAC tracks, we would
        // overwrite the previously written output. (This could be avoided by
        // picking a unique file name for every track, or by asking the user
        // which track to decode.)
        break
    }
}

/// Decode the metadata blocks until the streaminfo block is found.
fn get_streaminfo(track: &mp4parse::Track) -> Option<StreamInfo> {
    use mp4parse::{AudioCodecSpecific, SampleEntry};
    use claxon::metadata::MetadataBlock;

    let audio_entry = match &track.data {
        &Some(SampleEntry::Audio(ref ae)) => ae,
        _ => panic!("expected to find audio entry in FLAC track"),
    };

    let flac_box = match &audio_entry.codec_specific {
        &AudioCodecSpecific::FLACSpecificBox(ref fb) => fb,
        _ => return None,
    };

    for raw_block in &flac_box.blocks {
        let len = raw_block.data.len() as u32;
        let mut cursor = io::Cursor::new(&raw_block.data);
        let result = read_metadata_block(&mut cursor, raw_block.block_type, len);
        match result.expect("failed to decode metadata block") {
            MetadataBlock::StreamInfo(si) => return Some(si),
            _ => {}
        }
    }

    None
}

/// Decode a number of FLAC frames. Takes an input `io::Read` and returns it.
fn decode_frames<R, W>(input: R,
                       streaminfo: &StreamInfo,
                       num_frames: u32,
                       wav_writer: &mut WavWriter<W>)
                       -> R
where R: io::Read, W: io::Write + io::Seek {
    // The simplest way to read frames now, is unfortunately to double buffer.
    // (The input `R` is a buffered reader.) This might be avoided by not
    // wrapping the original file in an `io::BufReader`, but in a Claxon
    // `BufferedReader`. It would have to implement `io::Read` then.
    let buffered_reader = claxon::input::BufferedReader::new(input);
    let mut frame_reader = claxon::frame::FrameReader::new(buffered_reader);
    let mut buffer = Vec::with_capacity(streaminfo.max_block_size as usize);

    for _ in 0..num_frames {
        // TODO There should be a read_next method too that does not tolerate
        // EOF.
        let result = frame_reader.read_next_or_eof(buffer);
        let block = result.expect("failed to decode frame").expect("unexpected EOF");

        // TODO: Here we assume that we are decoding a stereo stream, which
        // is wrong, but very convenient, as there is no interleaved sample
        // iterator for `Block`. One should be added.
        for (sl, sr) in block.stereo_samples() {
            wav_writer.write_sample(sl).expect("failed to write wav file");
            wav_writer.write_sample(sr).expect("failed to write wav file");
        }

        buffer = block.into_buffer();
    }

    // Strip off the frame reader and buffered reader to get back the original
    // reader.
    frame_reader.into_inner().into_inner()
}

fn main() {
    for fname in env::args().skip(1) {
        decode_file(&Path::new(&fname));
    }
}
<commit_msg>Iterate mp4 chunks the right way<commit_after>// Claxon -- A FLAC decoding library in Rust
// Copyright 2017 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

// This file contains a minimal example of using Claxon with mp4parse and Hound
// to decode a flac stream inside an MP4 (.mp4 or .m4a) container to a wav file.

extern crate claxon;
extern crate hound;
extern crate mp4parse;

use std::env;
use std::fs::File;
use std::io::Seek;
use std::io;
use std::path::Path;

use claxon::metadata::read_metadata_block;
use claxon::metadata::StreamInfo;
use hound::{WavSpec, WavWriter};
use mp4parse::CodecType;

fn decode_file(fname: &Path) {
    // Create a file to read the mp4 structure from. We will later need to seek
    // in this file to get to the parts that contain FLAC data.
    let file = File::open(fname).expect("failed to open mp4 file");
    let mut bufread = io::BufReader::new(file);

    // Parse the mp4 metadata.
    let mut context = mp4parse::MediaContext::new();
    mp4parse::read_mp4(&mut bufread, &mut context).expect("failed to decode mp4");

    // An MP4 file contains one or more tracks. A track can contain a single
    // FLAC stream. Iterate over those.
    for track in &context.tracks {
        if track.codec_type != CodecType::FLAC {
            continue
        }

        let streaminfo = get_streaminfo(track).expect("missing streaminfo");

        // Build a wav writer to write the decoded track to a wav file.
        let spec = WavSpec {
            channels: streaminfo.channels as u16,
            sample_rate: streaminfo.sample_rate,
            bits_per_sample: streaminfo.bits_per_sample as u16,
            sample_format: hound::SampleFormat::Int,
        };

        let fname_wav = fname.with_extension("wav");
        let opt_wav_writer = WavWriter::create(fname_wav, spec);
        let mut wav_writer = opt_wav_writer.expect("failed to create wav file");

        // The data in an MP4 file is split into "chunks", for which we can get
        // the file offset where the chunk starts. Every chunk contains one or
        // more "samples", which is one FLAC frame. These frames are all stored
        // adjacently in the chunk, so we can read them with a fingle frame
        // reader.

        // The "stco" in mp4parse's Track stands for "Chunk Offset Box", "stsc"
        // stands for "Sample to Chunk Box", which actually tells per chunk how
        // many samples it contains, not per sample in which chunk it is.
        let chunk_offsets = &track.stco.as_ref().expect("missing chunk offset box").offsets;
        let chunk_samples = &track.stsc.as_ref().expect("missing sample to chunk box").samples;

        let mut samples_iter = chunk_samples.iter();
        let mut next_samples = samples_iter.next();
        let mut samples_per_chunk = 0;

        // Iterate over all chunks in this track. We need all chunks, and every
        // chunk is present in the chunk offset box.
        for (i, offset) in chunk_offsets.iter().enumerate() {
            bufread.seek(io::SeekFrom::Start(*offset)).expect("failed to seek to chunk");

            // For some chunks, the "Sample to Chunk Box" stores details about
            // how many "samples" (FLAC frames) there are per chunk. When there
            // is no such data for a chunk, the samples per chunk is the same as
            // for the previous chunk.
            next_samples = next_samples.and_then(|ns| {
                // The first_chunk field is a 1-based index, not 0-based.
                if ns.first_chunk == 1 + i as u32 {
                    samples_per_chunk = ns.samples_per_chunk;
                    samples_iter.next()
                } else {
                    Some(ns)
                }
            });

            bufread = decode_frames(bufread, &streaminfo, samples_per_chunk, &mut wav_writer);
        }

        // Stop iterating over tracks; if there are more FLAC tracks, we would
        // overwrite the previously written output. (This could be avoided by
        // picking a unique file name for every track, or by asking the user
        // which track to decode.)
        break
    }
}

/// Decode the metadata blocks until the streaminfo block is found.
fn get_streaminfo(track: &mp4parse::Track) -> Option<StreamInfo> {
    use mp4parse::{AudioCodecSpecific, SampleEntry};
    use claxon::metadata::MetadataBlock;

    let audio_entry = match &track.data {
        &Some(SampleEntry::Audio(ref ae)) => ae,
        _ => panic!("expected to find audio entry in FLAC track"),
    };

    let flac_box = match &audio_entry.codec_specific {
        &AudioCodecSpecific::FLACSpecificBox(ref fb) => fb,
        _ => return None,
    };

    for raw_block in &flac_box.blocks {
        let len = raw_block.data.len() as u32;
        let mut cursor = io::Cursor::new(&raw_block.data);
        let result = read_metadata_block(&mut cursor, raw_block.block_type, len);
        match result.expect("failed to decode metadata block") {
            MetadataBlock::StreamInfo(si) => return Some(si),
            _ => {}
        }
    }

    None
}

/// Decode a number of FLAC frames. Takes an input `io::Read` and returns it.
fn decode_frames<R, W>(input: R,
                       streaminfo: &StreamInfo,
                       num_frames: u32,
                       wav_writer: &mut WavWriter<W>)
                       -> R
where R: io::Read, W: io::Write + io::Seek {
    // The simplest way to read frames now, is unfortunately to double buffer.
    // (The input `R` is a buffered reader.) This might be avoided by not
    // wrapping the original file in an `io::BufReader`, but in a Claxon
    // `BufferedReader`. It would have to implement `io::Read` then.
    let buffered_reader = claxon::input::BufferedReader::new(input);
    let mut frame_reader = claxon::frame::FrameReader::new(buffered_reader);
    let mut buffer = Vec::with_capacity(streaminfo.max_block_size as usize);

    for _ in 0..num_frames {
        // TODO There should be a read_next method too that does not tolerate
        // EOF.
        let result = frame_reader.read_next_or_eof(buffer);
        let block = result.expect("failed to decode frame").expect("unexpected EOF");

        // TODO: Here we assume that we are decoding a stereo stream, which
        // is wrong, but very convenient, as there is no interleaved sample
        // iterator for `Block`. One should be added.
        for (sl, sr) in block.stereo_samples() {
            wav_writer.write_sample(sl).expect("failed to write wav file");
            wav_writer.write_sample(sr).expect("failed to write wav file");
        }

        buffer = block.into_buffer();
    }

    // Strip off the frame reader and buffered reader to get back the original
    // reader.
    frame_reader.into_inner().into_inner()
}

fn main() {
    for fname in env::args().skip(1) {
        decode_file(&Path::new(&fname));
    }
}
<|endoftext|>


<commit_before>extern crate gcc;
extern crate syntex_syntax as syntax;

use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::iter;
use std::path::{Path, PathBuf};

use syntax::ast;
use syntax::diagnostic::SpanHandler;
use syntax::parse::token::InternedString;
use syntax::attr::{self, ReprAttr};
use syntax::parse::{self, ParseSess};
use syntax::visit::{self, Visitor};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

struct TestGenerator<'a> {
    target: String,
    rust: Box<Write>,
    c: Box<Write>,
    sh: &'a SpanHandler,
    structs: HashSet<String>,
}

struct StructFinder {
    structs: HashSet<String>,
}

impl<'a> TestGenerator<'a> {
    fn defines(&self) -> Vec<&'static str> {
        let mut ret = Vec::new();
        if self.target.contains("unknown-linux") {
            ret.push("_GNU_SOURCE");
        }
        if self.target.contains("msvc") {
            ret.push("alignof __alignof");
        }
        return ret
    }

    fn headers(&self) -> Vec<&'static str> {
        let mut base = Vec::new();

        if self.target.contains("windows") {
            base.push("windows.h");
        }

        base.extend(&[
            "errno.h",
            "fcntl.h",
            "limits.h",
            "stddef.h",
            "stdint.h",
            "stdio.h",
            "stdlib.h",
            "sys/stat.h",
            "sys/types.h",
            "time.h",
            "wchar.h",
        ]);

        if self.target.contains("apple-darwin") {
            base.push("mach/mach_time.h");
        }

        if self.target.contains("unknown-linux") {
            base.push("linux/if_packet.h");
            base.push("net/ethernet.h");
        }

        if self.target.contains("windows") {
            base.push("ws2tcpip.h");
        } else {
            base.push("glob.h");
            base.push("ifaddrs.h");
            base.push("net/if.h");
            base.push("netdb.h");
            base.push("netinet/in.h");
            base.push("netinet/ip.h");
            base.push("netinet/tcp.h");
            base.push("pthread.h");
            base.push("signal.h");
            base.push("stdalign.h");
            base.push("sys/mman.h");
            base.push("sys/resource.h");
            base.push("sys/socket.h");
            base.push("sys/time.h");
            base.push("sys/un.h");
            base.push("utime.h");
            base.push("unistd.h");
        }

        return base
    }

    fn rust2c(&self, ty: &str) -> String {
        match ty {
            t if t.starts_with("c_") => {
                match &ty[2..].replace("long", " long")[..] {
                    s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
                    "short" => format!("short"),
                    s if s.starts_with("s") => format!("signed {}", &s[1..]),
                    s => s.to_string(),
                }
            }
            "ip6_mreq" => "struct ipv6_mreq".to_string(),
            "glob_t" => "glob_t".to_string(),
            t if t.starts_with("pthread") => t.to_string(),

            t if self.structs.contains(t) => format!("struct {}", t),

            t => t.to_string(),
        }
    }

    fn rust2cfield(&self, struct_: &str, field: &str) -> String {
        match field {
            s if s.ends_with("_nsec") && struct_ == "stat" => {
                if self.target.contains("apple-darwin") {
                    s.replace("_nsec", "spec.tv_nsec")
                } else {
                    s.replace("e_nsec", ".tv_nsec")
                }
            }
            s => s.to_string(),
        }
    }

    fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
        let mut ret = Vec::new();
        let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
            ("x86_64", "64")
        } else if self.target.starts_with("i686") {
            ("x86", "32")
        } else {
            panic!("unknown arch/pointer width: {}", self.target)
        };
        let (os, family, env) = if self.target.contains("unknown-linux") {
            ("linux", "unix", "gnu")
        } else if self.target.contains("apple-darwin") {
            ("macos", "unix", "")
        } else if self.target.contains("windows-msvc") {
            ("windows", "windows", "msvc")
        } else if self.target.contains("windows-gnu") {
            ("windows", "windows", "gnu")
        } else {
            panic!("unknown os/family width: {}", self.target)
        };

        ret.push((family, None));
        ret.push(("target_os", Some(os)));
        ret.push(("target_family", Some(family)));
        ret.push(("target_arch", Some(arch)));
        // skip endianness
        ret.push(("target_pointer_width", Some(target_pointer_width)));
        ret.push(("target_env", Some(env)));

        return ret
    }
}

fn main() {
    // Prep the test generator
    let target = t!(env::var("TARGET"));
    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
    let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
    let sess = ParseSess::new();
    let mut tg = TestGenerator {
        target: target,
        rust: Box::new(rust_out),
        c: Box::new(c_out),
        sh: &sess.span_diagnostic,
        structs: HashSet::new(),
    };

    // Parse the libc crate
    let src = Path::new("../src/lib.rs");
    let cfg = Vec::new();
    let mut krate = parse::parse_crate_from_file(src, cfg, &sess);

    // Strip the crate down to just what's configured for our target
    for (k, v) in tg.cfg_list() {
        let s = InternedString::new;
        krate.config.push(match v {
            Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
            None => attr::mk_word_item(s(k)),
        });
    }
    let mut gated_cfgs = Vec::new();
    let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
                                                         krate,
                                                         &mut gated_cfgs);

    // Probe the crate to find all structs (used to convert type names to names
    // in C).
    let mut structs = StructFinder {
        structs: HashSet::new(),
    };
    visit::walk_crate(&mut structs, &krate);
    tg.structs = structs.structs;

    // Prep the C file by emitting header stuff
    for define in tg.defines() {
        t!(writeln!(tg.c, "#define {}", define));
    }
    for header in tg.headers() {
        t!(writeln!(tg.c, "#include <{}>", header));
    }

    // Walk the crate, emitting test cases for everything found
    visit::walk_crate(&mut tg, &krate);

    // Compile our C shim to be linked into tests
    let mut cfg = gcc::Config::new();
    cfg.file(out.join("all.c"));

    if tg.target.contains("msvc") {
        cfg.flag("/W3").flag("/Wall").flag("/WX");
    } else {
        cfg.flag("-Wall").flag("-Wextra").flag("-Werror")
           .arg("-Wno-unused-parameter");
    }

    drop(tg);
    cfg.compile("liball.a");
}

impl<'a> TestGenerator<'a> {
    fn test_type(&mut self, ty: &str) {
        match ty {
            "sighandler_t" => return,
            _ => {}
        }
        let c = self.rust2c(ty);
        self.test_size_align(ty, &c);
    }

    fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
        let cty = self.rust2c(ty);
        self.test_size_align(ty, &cty);

        t!(writeln!(self.rust, r#"
            #[test]
            fn field_offset_size_{ty}() {{
        "#, ty = ty));
        for field in s.fields.iter() {
            let name = match field.node.kind {
                ast::NamedField(name, ast::Public) => name,
                ast::NamedField(_, ast::Inherited) => continue,
                ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
            };

            let cfield = self.rust2cfield(ty, &name.to_string());

            t!(writeln!(self.c, r#"
                uint64_t __test_offset_{ty}_{rust_field}(void) {{
                    return offsetof({cty}, {c_field});
                }}
                uint64_t __test_size_{ty}_{rust_field}(void) {{
                    {cty}* foo = NULL;
                    return sizeof(foo->{c_field});
                }}
            "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
            t!(writeln!(self.rust, r#"
                extern {{
                    fn __test_offset_{ty}_{field}() -> u64;
                    fn __test_size_{ty}_{field}() -> u64;
                }}
                unsafe {{
                    let foo = 0 as *const {ty};
                    same(offset_of!({ty}, {field}),
                         __test_offset_{ty}_{field}(),
                         "field offset {field} of {ty}");
                    same(mem::size_of_val(&(*foo).{field}) as u64,
                         __test_size_{ty}_{field}(),
                         "field size {field} of {ty}");
                }}
            "#, ty = ty, field = name));
        }
        t!(writeln!(self.rust, r#"
            }}
        "#));
    }

    fn test_size_align(&mut self, rust: &str, c: &str) {
        t!(writeln!(self.c, r#"
            uint64_t __test_size_{ty}(void) {{ return sizeof({cty}); }}
            uint64_t __test_align_{ty}(void) {{ return alignof({cty}); }}
        "#, ty = rust, cty = c));
        t!(writeln!(self.rust, r#"
            #[test]
            fn size_align_{ty}() {{
                extern {{
                    fn __test_size_{ty}() -> u64;
                    fn __test_align_{ty}() -> u64;
                }}
                unsafe {{
                    same(mem::size_of::<{ty}>() as u64,
                         __test_size_{ty}(), "size");
                    same(align::<{ty}>() as u64,
                         __test_align_{ty}(), "align");
                }}
            }}
        "#, ty = rust));
    }

    fn test_const(&mut self, name: &str, rust_ty: &str) {
        let cty = self.rust2c(&rust_ty.replace("*mut ", "")
                                      .replace("*const ", ""));
        let ptrs = rust_ty.matches("*").count();
        let cty = format!("{}{}", cty,
                          iter::repeat("*").take(ptrs).collect::<String>());
        let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
        t!(writeln!(self.c, r#"
            int __test_const_{name}({cty} *out) {{
                int ret = 0;
                #if defined({name})
                    *out = {cast}({name});
                    ret = 1;
                #endif
                return ret;
            }}
        "#, name = name, cast = cast, cty = cty));
        t!(writeln!(self.rust, r#"
            #[test]
            fn const_{name}() {{
                extern {{
                    fn __test_const_{name}(out: *mut {ty}) -> c_int;
                }}
                unsafe {{
                    let mut o = mem::zeroed();
                    if __test_const_{name}(&mut o) == 0 {{
                        panic!("not defined");
                    }} else {{
                        same({name}, o, "value");
                    }}
                }}
            }}
        "#, ty = rust_ty, name = name));
    }

    fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
         assert!(generics.lifetimes.len() == 0);
         assert!(generics.ty_params.len() == 0);
         assert!(generics.where_clause.predicates.len() == 0);
    }

    fn ty2name(&self, ty: &ast::Ty) -> String {
        match ty.node {
            ast::TyPath(_, ref path) => {
                path.segments.last().unwrap().identifier.to_string()
            }
            ast::TyPtr(ref t) => {
                format!("*{} {}", match t.mutbl {
                    ast::MutImmutable => "const",
                    ast::MutMutable => "mut",
                }, self.ty2name(&t.ty))
            }
            _ => panic!("unknown ty {:?}", ty),
        }
    }
}

impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemTy(_, ref generics) => {
                 self.assert_no_generics(i, generics);
                 self.test_type(&i.ident.to_string());
             }

             ast::ItemStruct(ref s, ref generics) => {
                 self.assert_no_generics(i, generics);
                 let is_c = i.attrs.iter().any(|a| {
                    attr::find_repr_attrs(self.sh, a).iter().any(|a| {
                        *a == ReprAttr::ReprExtern
                    })
                 });
                 if !is_c {
                     panic!("{} is not marked #[repr(C)]", i.ident);
                 }
                 self.test_struct(&i.ident.to_string(), s);
             }

             ast::ItemConst(ref ty, _) => {
                 let ty = self.ty2name(ty);
                 self.test_const(&i.ident.to_string(), &ty);
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}

impl<'v> Visitor<'v> for StructFinder {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemStruct(..) => {
                 self.structs.insert(i.ident.to_string());
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}
<commit_msg>arg => flag<commit_after>extern crate gcc;
extern crate syntex_syntax as syntax;

use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::iter;
use std::path::{Path, PathBuf};

use syntax::ast;
use syntax::diagnostic::SpanHandler;
use syntax::parse::token::InternedString;
use syntax::attr::{self, ReprAttr};
use syntax::parse::{self, ParseSess};
use syntax::visit::{self, Visitor};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

struct TestGenerator<'a> {
    target: String,
    rust: Box<Write>,
    c: Box<Write>,
    sh: &'a SpanHandler,
    structs: HashSet<String>,
}

struct StructFinder {
    structs: HashSet<String>,
}

impl<'a> TestGenerator<'a> {
    fn defines(&self) -> Vec<&'static str> {
        let mut ret = Vec::new();
        if self.target.contains("unknown-linux") {
            ret.push("_GNU_SOURCE");
        }
        if self.target.contains("msvc") {
            ret.push("alignof __alignof");
        }
        return ret
    }

    fn headers(&self) -> Vec<&'static str> {
        let mut base = Vec::new();

        if self.target.contains("windows") {
            base.push("windows.h");
        }

        base.extend(&[
            "errno.h",
            "fcntl.h",
            "limits.h",
            "stddef.h",
            "stdint.h",
            "stdio.h",
            "stdlib.h",
            "sys/stat.h",
            "sys/types.h",
            "time.h",
            "wchar.h",
        ]);

        if self.target.contains("apple-darwin") {
            base.push("mach/mach_time.h");
        }

        if self.target.contains("unknown-linux") {
            base.push("linux/if_packet.h");
            base.push("net/ethernet.h");
        }

        if self.target.contains("windows") {
            base.push("winsock.h");
        } else {
            base.push("glob.h");
            base.push("ifaddrs.h");
            base.push("net/if.h");
            base.push("netdb.h");
            base.push("netinet/in.h");
            base.push("netinet/ip.h");
            base.push("netinet/tcp.h");
            base.push("pthread.h");
            base.push("signal.h");
            base.push("stdalign.h");
            base.push("sys/mman.h");
            base.push("sys/resource.h");
            base.push("sys/socket.h");
            base.push("sys/time.h");
            base.push("sys/un.h");
            base.push("utime.h");
            base.push("unistd.h");
        }

        return base
    }

    fn rust2c(&self, ty: &str) -> String {
        match ty {
            t if t.starts_with("c_") => {
                match &ty[2..].replace("long", " long")[..] {
                    s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
                    "short" => format!("short"),
                    s if s.starts_with("s") => format!("signed {}", &s[1..]),
                    s => s.to_string(),
                }
            }
            "ip6_mreq" => "struct ipv6_mreq".to_string(),
            "glob_t" => "glob_t".to_string(),
            t if t.starts_with("pthread") => t.to_string(),

            t if self.structs.contains(t) => format!("struct {}", t),

            t => t.to_string(),
        }
    }

    fn rust2cfield(&self, struct_: &str, field: &str) -> String {
        match field {
            s if s.ends_with("_nsec") && struct_ == "stat" => {
                if self.target.contains("apple-darwin") {
                    s.replace("_nsec", "spec.tv_nsec")
                } else {
                    s.replace("e_nsec", ".tv_nsec")
                }
            }
            s => s.to_string(),
        }
    }

    fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
        let mut ret = Vec::new();
        let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
            ("x86_64", "64")
        } else if self.target.starts_with("i686") {
            ("x86", "32")
        } else {
            panic!("unknown arch/pointer width: {}", self.target)
        };
        let (os, family, env) = if self.target.contains("unknown-linux") {
            ("linux", "unix", "gnu")
        } else if self.target.contains("apple-darwin") {
            ("macos", "unix", "")
        } else if self.target.contains("windows-msvc") {
            ("windows", "windows", "msvc")
        } else if self.target.contains("windows-gnu") {
            ("windows", "windows", "gnu")
        } else {
            panic!("unknown os/family width: {}", self.target)
        };

        ret.push((family, None));
        ret.push(("target_os", Some(os)));
        ret.push(("target_family", Some(family)));
        ret.push(("target_arch", Some(arch)));
        // skip endianness
        ret.push(("target_pointer_width", Some(target_pointer_width)));
        ret.push(("target_env", Some(env)));

        return ret
    }
}

fn main() {
    // Prep the test generator
    let target = t!(env::var("TARGET"));
    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
    let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
    let sess = ParseSess::new();
    let mut tg = TestGenerator {
        target: target,
        rust: Box::new(rust_out),
        c: Box::new(c_out),
        sh: &sess.span_diagnostic,
        structs: HashSet::new(),
    };

    // Parse the libc crate
    let src = Path::new("../src/lib.rs");
    let cfg = Vec::new();
    let mut krate = parse::parse_crate_from_file(src, cfg, &sess);

    // Strip the crate down to just what's configured for our target
    for (k, v) in tg.cfg_list() {
        let s = InternedString::new;
        krate.config.push(match v {
            Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
            None => attr::mk_word_item(s(k)),
        });
    }
    let mut gated_cfgs = Vec::new();
    let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
                                                         krate,
                                                         &mut gated_cfgs);

    // Probe the crate to find all structs (used to convert type names to names
    // in C).
    let mut structs = StructFinder {
        structs: HashSet::new(),
    };
    visit::walk_crate(&mut structs, &krate);
    tg.structs = structs.structs;

    // Prep the C file by emitting header stuff
    for define in tg.defines() {
        t!(writeln!(tg.c, "#define {}", define));
    }
    for header in tg.headers() {
        t!(writeln!(tg.c, "#include <{}>", header));
    }

    // Walk the crate, emitting test cases for everything found
    visit::walk_crate(&mut tg, &krate);

    // Compile our C shim to be linked into tests
    let mut cfg = gcc::Config::new();
    cfg.file(out.join("all.c"));

    if tg.target.contains("msvc") {
        cfg.flag("/W3").flag("/Wall").flag("/WX");
    } else {
        cfg.flag("-Wall").flag("-Wextra").flag("-Werror")
           .flag("-Wno-unused-parameter");
    }

    drop(tg);
    cfg.compile("liball.a");
}

impl<'a> TestGenerator<'a> {
    fn test_type(&mut self, ty: &str) {
        match ty {
            "sighandler_t" => return,
            _ => {}
        }
        let c = self.rust2c(ty);
        self.test_size_align(ty, &c);
    }

    fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
        let cty = self.rust2c(ty);
        self.test_size_align(ty, &cty);

        t!(writeln!(self.rust, r#"
            #[test]
            fn field_offset_size_{ty}() {{
        "#, ty = ty));
        for field in s.fields.iter() {
            let name = match field.node.kind {
                ast::NamedField(name, ast::Public) => name,
                ast::NamedField(_, ast::Inherited) => continue,
                ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
            };

            let cfield = self.rust2cfield(ty, &name.to_string());

            t!(writeln!(self.c, r#"
                uint64_t __test_offset_{ty}_{rust_field}(void) {{
                    return offsetof({cty}, {c_field});
                }}
                uint64_t __test_size_{ty}_{rust_field}(void) {{
                    {cty}* foo = NULL;
                    return sizeof(foo->{c_field});
                }}
            "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
            t!(writeln!(self.rust, r#"
                extern {{
                    fn __test_offset_{ty}_{field}() -> u64;
                    fn __test_size_{ty}_{field}() -> u64;
                }}
                unsafe {{
                    let foo = 0 as *const {ty};
                    same(offset_of!({ty}, {field}),
                         __test_offset_{ty}_{field}(),
                         "field offset {field} of {ty}");
                    same(mem::size_of_val(&(*foo).{field}) as u64,
                         __test_size_{ty}_{field}(),
                         "field size {field} of {ty}");
                }}
            "#, ty = ty, field = name));
        }
        t!(writeln!(self.rust, r#"
            }}
        "#));
    }

    fn test_size_align(&mut self, rust: &str, c: &str) {
        t!(writeln!(self.c, r#"
            uint64_t __test_size_{ty}(void) {{ return sizeof({cty}); }}
            uint64_t __test_align_{ty}(void) {{ return alignof({cty}); }}
        "#, ty = rust, cty = c));
        t!(writeln!(self.rust, r#"
            #[test]
            fn size_align_{ty}() {{
                extern {{
                    fn __test_size_{ty}() -> u64;
                    fn __test_align_{ty}() -> u64;
                }}
                unsafe {{
                    same(mem::size_of::<{ty}>() as u64,
                         __test_size_{ty}(), "size");
                    same(align::<{ty}>() as u64,
                         __test_align_{ty}(), "align");
                }}
            }}
        "#, ty = rust));
    }

    fn test_const(&mut self, name: &str, rust_ty: &str) {
        let cty = self.rust2c(&rust_ty.replace("*mut ", "")
                                      .replace("*const ", ""));
        let ptrs = rust_ty.matches("*").count();
        let cty = format!("{}{}", cty,
                          iter::repeat("*").take(ptrs).collect::<String>());
        let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
        t!(writeln!(self.c, r#"
            int __test_const_{name}({cty} *out) {{
                int ret = 0;
                #if defined({name})
                    *out = {cast}({name});
                    ret = 1;
                #endif
                return ret;
            }}
        "#, name = name, cast = cast, cty = cty));
        t!(writeln!(self.rust, r#"
            #[test]
            fn const_{name}() {{
                extern {{
                    fn __test_const_{name}(out: *mut {ty}) -> c_int;
                }}
                unsafe {{
                    let mut o = mem::zeroed();
                    if __test_const_{name}(&mut o) == 0 {{
                        panic!("not defined");
                    }} else {{
                        same({name}, o, "value");
                    }}
                }}
            }}
        "#, ty = rust_ty, name = name));
    }

    fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
         assert!(generics.lifetimes.len() == 0);
         assert!(generics.ty_params.len() == 0);
         assert!(generics.where_clause.predicates.len() == 0);
    }

    fn ty2name(&self, ty: &ast::Ty) -> String {
        match ty.node {
            ast::TyPath(_, ref path) => {
                path.segments.last().unwrap().identifier.to_string()
            }
            ast::TyPtr(ref t) => {
                format!("*{} {}", match t.mutbl {
                    ast::MutImmutable => "const",
                    ast::MutMutable => "mut",
                }, self.ty2name(&t.ty))
            }
            _ => panic!("unknown ty {:?}", ty),
        }
    }
}

impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemTy(_, ref generics) => {
                 self.assert_no_generics(i, generics);
                 self.test_type(&i.ident.to_string());
             }

             ast::ItemStruct(ref s, ref generics) => {
                 self.assert_no_generics(i, generics);
                 let is_c = i.attrs.iter().any(|a| {
                    attr::find_repr_attrs(self.sh, a).iter().any(|a| {
                        *a == ReprAttr::ReprExtern
                    })
                 });
                 if !is_c {
                     panic!("{} is not marked #[repr(C)]", i.ident);
                 }
                 self.test_struct(&i.ident.to_string(), s);
             }

             ast::ItemConst(ref ty, _) => {
                 let ty = self.ty2name(ty);
                 self.test_const(&i.ident.to_string(), &ty);
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}

impl<'v> Visitor<'v> for StructFinder {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemStruct(..) => {
                 self.structs.insert(i.ident.to_string());
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}
<|endoftext|>


<commit_before>#[derive(Clone, Debug)]
/// Expression enum.
pub enum Expression {
    Unary(UnaryExpr),
    Binary(BinaryExpr),
    Literal(Literal),
    Identifier(Identifier),
    FunctionCall(Call),
    Sub(Sub),
    Cast(Cast),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Unary Operators
pub enum UnaryOperator {
    Not,
    Negative,
    BitwiseNot,
}

#[derive(Debug, Clone)]
/// Unary Expression.
pub struct UnaryExpr {
    pub operator: UnaryOperator,
    pub right: Box<Expression>,
}

#[derive(Copy, Clone, Debug)]
/// Enum representing binary operators.
pub enum BinaryOperator {
    Arithmetic(ArithmeticOperator),
    Logical(LogicalOperator),
    Relational(RelationalOperator),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Arithmetic Operators
pub enum ArithmeticOperator {
    Add,
    Subtract,
    Multiply,
    Divide,
    Modulo,
    BitwiseAnd,
    BitwiseOr,
    BitwiseXor,
    LeftShift,
    RightShift,
    Increment,
    Decrement,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Logical Operators
pub enum LogicalOperator {
    And,
    Or,
    Xor,
    ShortAnd,
    ShortOr,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Relational Operators
pub enum RelationalOperator {
    Equal,
    NotEqual,
    GreaterThan,
    LessThan,
    GreaterOrEqual,
    LessOrEqual,
}

#[derive(Debug, Clone)]
/// Binary Expression.
pub struct BinaryExpr {
    pub left: Box<Expression>,
    pub operator: BinaryOperator,
    pub right: Box<Expression>,
}

#[derive(Debug, Clone)]
/// Subfield access expression. (i.e. "left.right")
pub struct Sub {
    pub left: Box<Expression>,
    pub right: Box<Expression>,
}

#[derive(Debug, Clone)]
/// Cast expression struct (i.e. "value to type")
pub struct Cast {
    pub value: Box<Expression>,
    pub to_type: Type,
}

#[derive(Debug, Clone)]
/// Literal struct.
pub struct Literal {
    pub literal: String,
}

#[derive(Debug, Clone)]
/// Identifier struct.
pub struct Identifier {
    pub id: String,
}

#[derive(Debug, Clone)]
/// Type struct.
pub struct Type {
    pub id: String,
    pub type_parameters: Vec<Type>,
}

#[derive(Debug, Clone)]
/// Function call.
pub struct Call {
    pub callee: Identifier,
    pub args: Vec<Box<Expression>>,
}

#[derive(Debug, Clone)]
/// Enum for statements.
pub enum Statement {
    Block(Block),
    Expression(Expression),
    Assignment(Assignment),
    WhileLoop(WhileLoop),
    ForLoop(ForLoop),
    FnDef(FunctionDefinition),
    ClassDef(ClassDeclaration),
    TraitDef(TraitDeclaration),
    EnumDef(EnumDeclaration),
    ConstDef(Constant),
    Return(Return),
    Break(Break),
    Continue(Continue),
    SingleCondition(Condition),
    Conditional(Conditional),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Different types of assignments, one for mutability, the other for immutability.
pub enum Assigner {
    Let,
    Var,
}

#[derive(Debug, Clone)]
/// Assignment statement.
pub struct Assignment {
    pub left: Identifier,
    pub assign_type: Assigner,
    pub right: Box<Expression>,
    pub declared_type: Option<Type>,
}

#[derive(Debug, Clone)]
/// Block of statements
pub struct Block {
    pub statements: Vec<Box<Statement>>,
}

#[derive(Debug, Clone)]
/// Single conditional statement.
pub struct Condition {
    pub condition: Expression,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Conditional block
pub struct Conditional {
    pub conditions: Vec<Condition>,
}

#[derive(Debug, Clone)]
/// While loop struct.
pub struct WhileLoop {
    pub id: LoopAnnotation,
    pub condition: Expression,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// For loop struct.
pub struct ForLoop {
    pub id: LoopAnnotation,
    pub assignment: Expression,
    pub source_var: Expression,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Function defining struct.
pub struct FunctionDefinition {
    pub declared_type: Option<Type>,
    pub id: Identifier,
    pub args: Vec<(Identifier, Option<Type>)>, //(name, type)
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Class declaration struct.
pub struct ClassDeclaration {
    pub generics: Vec<Type>,
    pub id: Identifier,
    pub traits_implemented: Vec<Identifier>,
    pub fields: Vec<(Identifier, Type)>,
}

#[derive(Debug, Clone)]
/// Trait declaration struct.
pub struct TraitDeclaration {
    pub generics: Vec<Type>,
    pub id: Identifier,
    pub requires: Vec<Identifier>,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Enum (tagged union) declaration struct.
pub struct EnumDeclaration {
    pub generics: Vec<Type>,
    pub id: Identifier,
    pub variants: Vec<ClassDeclaration>,
}

#[derive(Debug, Clone)]
/// Constant struct.
pub struct Constant {
    pub id: Identifier,
    pub declared_type: Option<Type>,
    pub val: Box<Expression>,
}

#[derive(Debug, Clone)]
/// Return statement struct.
pub struct Return {
    pub val: Box<Expression>
}

#[derive(Debug, Clone)]
/// Loop annotation struct
pub struct LoopAnnotation {
    pub id: String,
}

#[derive(Debug, Clone)]
/// Break statement struct.
pub struct Break {
    pub id: LoopAnnotation,
}

#[derive(Debug, Clone)]
/// Continue statement struct.
pub struct Continue {
    pub id: LoopAnnotation,
}

#[derive(Debug, Clone)]
/// Module struct
/// Module is not in `Statement` because
/// users should not be able to define modules in a file.
/// Each Wright file should be it's own independent Module.
pub struct Module {
    pub id: Identifier,
    pub content: Block,
}

impl Module {
    pub fn new(name: String) -> Self {
        Module {
            id: Identifier{id: name},
            content: Block{statements: vec![]}
        }
    }
}

<commit_msg>Docs Update<commit_after>#[derive(Clone, Debug)]
/// Expression enum.
pub enum Expression {
    Unary(UnaryExpr),
    Binary(BinaryExpr),
    Literal(Literal),
    Identifier(Identifier),
    FunctionCall(Call),
    Sub(Sub),
    Cast(Cast),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Unary Operators.
pub enum UnaryOperator {
    Not,
    Negative,
    BitwiseNot,
}

#[derive(Debug, Clone)]
/// Unary Expression.
pub struct UnaryExpr {
    pub operator: UnaryOperator,
    pub right: Box<Expression>,
}

#[derive(Copy, Clone, Debug)]
/// Enum representing binary operators.
pub enum BinaryOperator {
    Arithmetic(ArithmeticOperator),
    Logical(LogicalOperator),
    Relational(RelationalOperator),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Arithmetic Operators.
pub enum ArithmeticOperator {
    Add,
    Subtract,
    Multiply,
    Divide,
    Modulo,
    BitwiseAnd,
    BitwiseOr,
    BitwiseXor,
    LeftShift,
    RightShift,
    Increment,
    Decrement,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Logical Operators.
pub enum LogicalOperator {
    And,
    Or,
    Xor,
    ShortAnd,
    ShortOr,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Relational Operators.
pub enum RelationalOperator {
    Equal,
    NotEqual,
    GreaterThan,
    LessThan,
    GreaterOrEqual,
    LessOrEqual,
}

#[derive(Debug, Clone)]
/// Binary Expression.
pub struct BinaryExpr {
    pub left: Box<Expression>,
    pub operator: BinaryOperator,
    pub right: Box<Expression>,
}

#[derive(Debug, Clone)]
/// Subfield access expression. (i.e. "left.right").
pub struct Sub {
    pub left: Box<Expression>,
    pub right: Box<Expression>,
}

#[derive(Debug, Clone)]
/// Cast expression struct (i.e. "value to type").
pub struct Cast {
    pub value: Box<Expression>,
    pub to_type: Type,
}

#[derive(Debug, Clone)]
/// Literal struct.
pub struct Literal {
    pub literal: String,
}

#[derive(Debug, Clone)]
/// Identifier struct.
pub struct Identifier {
    pub id: String,
}

#[derive(Debug, Clone)]
/// Type struct.
pub struct Type {
    pub id: String,
    pub type_parameters: Vec<Type>,
}

#[derive(Debug, Clone)]
/// Function call.
pub struct Call {
    pub callee: Identifier,
    pub args: Vec<Box<Expression>>,
}

#[derive(Debug, Clone)]
/// Enum for statements.
pub enum Statement {
    Block(Block),
    Expression(Expression),
    Assignment(Assignment),
    WhileLoop(WhileLoop),
    ForLoop(ForLoop),
    FnDef(FunctionDefinition),
    ClassDef(ClassDeclaration),
    TraitDef(TraitDeclaration),
    EnumDef(EnumDeclaration),
    ConstDef(Constant),
    Return(Return),
    Break(Break),
    Continue(Continue),
    SingleCondition(Condition),
    Conditional(Conditional),
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
/// Different types of assignments, one for mutability, the other for immutability.
pub enum Assigner {
    Let,
    Var,
}

#[derive(Debug, Clone)]
/// Assignment statement.
pub struct Assignment {
    pub left: Identifier,
    pub assign_type: Assigner,
    pub right: Box<Expression>,
    pub declared_type: Option<Type>,
}

#[derive(Debug, Clone)]
/// Block of statements.
pub struct Block {
    pub statements: Vec<Box<Statement>>,
}

#[derive(Debug, Clone)]
/// Single conditional statement.
pub struct Condition {
    pub condition: Expression,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Conditional block.
pub struct Conditional {
    pub conditions: Vec<Condition>,
}

#[derive(Debug, Clone)]
/// While loop struct.
pub struct WhileLoop {
    pub id: LoopAnnotation,
    pub condition: Expression,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// For loop struct.
pub struct ForLoop {
    pub id: LoopAnnotation,
    pub assignment: Expression,
    pub source_var: Expression,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Function defining struct.
pub struct FunctionDefinition {
    pub declared_type: Option<Type>,
    pub id: Identifier,
    pub args: Vec<(Identifier, Option<Type>)>, //(name, type)
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Class declaration struct.
pub struct ClassDeclaration {
    pub generics: Vec<Type>,
    pub id: Identifier,
    pub traits_implemented: Vec<Identifier>,
    pub fields: Vec<(Identifier, Type)>,
}

#[derive(Debug, Clone)]
/// Trait declaration struct.
pub struct TraitDeclaration {
    pub generics: Vec<Type>,
    pub id: Identifier,
    pub requires: Vec<Identifier>,
    pub block: Block,
}

#[derive(Debug, Clone)]
/// Enum (tagged union) declaration struct.
pub struct EnumDeclaration {
    pub generics: Vec<Type>,
    pub id: Identifier,
    pub variants: Vec<ClassDeclaration>,
}

#[derive(Debug, Clone)]
/// Constant struct.
pub struct Constant {
    pub id: Identifier,
    pub declared_type: Option<Type>,
    pub val: Box<Expression>,
}

#[derive(Debug, Clone)]
/// Return statement struct.
pub struct Return {
    pub val: Box<Expression>
}

#[derive(Debug, Clone)]
/// Loop annotation struct.
pub struct LoopAnnotation {
    pub id: String,
}

#[derive(Debug, Clone)]
/// Break statement struct.
pub struct Break {
    pub id: LoopAnnotation,
}

#[derive(Debug, Clone)]
/// Continue statement struct.
pub struct Continue {
    pub id: LoopAnnotation,
}

#[derive(Debug, Clone)]
/// Module struct.
/// Module is not in `Statement` because
/// users should not be able to define modules in a file.
/// Each Wright file should be it's own independent Module.
pub struct Module {
    pub id: Identifier,
    pub content: Block,
}

impl Module {
    pub fn new(name: String) -> Self {
        Module {
            id: Identifier{id: name},
            content: Block{statements: vec![]}
        }
    }
}

<|endoftext|>


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

//! Data structure measurement.

use libc::{c_void, size_t};
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, LinkedList, hash_state};
use std::hash::Hash;
use std::mem::{size_of, transmute};
use std::sync::Arc;
use std::rc::Rc;

use azure::azure_hl::Color;
use cssparser::Color as CSSParserColor;
use cssparser::RGBA;
use cursor::Cursor;
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D, Matrix4};
use euclid::length::Length;
use euclid::scale_factor::ScaleFactor;
use geometry::{PagePx, ViewportPx, Au};
use html5ever::tree_builder::QuirksMode;
use layers::geometry::DevicePixel;
use js::jsapi::Heap;
use js::rust::GCMethods;
use js::jsval::JSVal;
use logical_geometry::WritingMode;
use range::Range;
use string_cache::atom::Atom;
use url;

extern {
    // Get the size of a heap block.
    //
    // Ideally Rust would expose a function like this in std::rt::heap, which would avoid the
    // jemalloc dependence.
    //
    // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some
    // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice
    // this function doesn't modify the contents of the block that `ptr` points to, so we use
    // `*const c_void` here.
    fn je_malloc_usable_size(ptr: *const c_void) -> size_t;
}

// A wrapper for je_malloc_usable_size that handles `EMPTY` and returns `usize`.
pub fn heap_size_of(ptr: *const c_void) -> usize {
    if ptr == ::std::rt::heap::EMPTY as *const c_void {
        0
    } else {
        unsafe { je_malloc_usable_size(ptr) as usize }
    }
}

// The simplest trait for measuring the size of heap data structures. More complex traits that
// return multiple measurements -- e.g. measure text separately from images -- are also possible,
// and should be used when appropriate.
//
pub trait HeapSizeOf {
    /// Measure the size of any heap-allocated structures that hang off this value, but not the
    /// space taken up by the value itself (i.e. what size_of::<T> measures, more or less); that
    /// space is handled by the implementation of HeapSizeOf for Box<T> below.
    fn heap_size_of_children(&self) -> usize;
}

// There are two possible ways to measure the size of `self` when it's on the heap: compute it
// (with `::std::rt::heap::usable_size(::std::mem::size_of::<T>(), 0)`) or measure it directly
// using the heap allocator (with `heap_size_of`). We do the latter, for the following reasons.
//
// * The heap allocator is the true authority for the sizes of heap blocks; its measurement is
//   guaranteed to be correct. In comparison, size computations are error-prone. (For example, the
//   `rt::heap::usable_size` function used in some of Rust's non-default allocator implementations
//   underestimate the true usable size of heap blocks, which is safe in general but would cause
//   under-measurement here.)
//
// * If we measure something that isn't a heap block, we'll get a crash. This keeps us honest,
//   which is important because unsafe code is involved and this can be gotten wrong.
//
// However, in the best case, the two approaches should give the same results.
//
impl<T: HeapSizeOf> HeapSizeOf for Box<T> {
    fn heap_size_of_children(&self) -> usize {
        // Measure size of `self`.
        heap_size_of(&**self as *const T as *const c_void) + (**self).heap_size_of_children()
    }
}

impl HeapSizeOf for String {
    fn heap_size_of_children(&self) -> usize {
        heap_size_of(self.as_ptr() as *const c_void)
    }
}

impl<T: HeapSizeOf> HeapSizeOf for Option<T> {
    fn heap_size_of_children(&self) -> usize {
        match *self {
            None => 0,
            Some(ref x) => x.heap_size_of_children()
        }
    }
}

impl HeapSizeOf for url::Url {
    fn heap_size_of_children(&self) -> usize {
        let &url::Url { ref scheme, ref scheme_data, ref query, ref fragment } = self;
        scheme.heap_size_of_children() +
        scheme_data.heap_size_of_children() +
        query.heap_size_of_children() +
        fragment.heap_size_of_children()
    }
}

impl HeapSizeOf for url::SchemeData {
    fn heap_size_of_children(&self) -> usize {
        match self {
            &url::SchemeData::Relative(ref data) => data.heap_size_of_children(),
            &url::SchemeData::NonRelative(ref str) => str.heap_size_of_children()
        }
    }
}

impl HeapSizeOf for url::RelativeSchemeData {
    fn heap_size_of_children(&self) -> usize {
        let &url::RelativeSchemeData { ref username, ref password, ref host,
                                       ref port, ref default_port, ref path } = self;
        username.heap_size_of_children() +
        password.heap_size_of_children() +
        host.heap_size_of_children() +
        port.heap_size_of_children() +
        default_port.heap_size_of_children() +
        path.heap_size_of_children()
    }
}

impl HeapSizeOf for url::Host {
    fn heap_size_of_children(&self) -> usize {
        match self {
            &url::Host::Domain(ref str) => str.heap_size_of_children(),
            &url::Host::Ipv6(_) => 0
        }
    }
}

impl<T: HeapSizeOf, U: HeapSizeOf> HeapSizeOf for (T, U) {
    fn heap_size_of_children(&self) -> usize {
        self.0.heap_size_of_children() + self.1.heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for Arc<T> {
    fn heap_size_of_children(&self) -> usize {
        (**self).heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for RefCell<T> {
    fn heap_size_of_children(&self) -> usize {
        self.borrow().heap_size_of_children()
    }
}

impl<T: HeapSizeOf + Copy> HeapSizeOf for Cell<T> {
    fn heap_size_of_children(&self) -> usize {
        self.get().heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for Vec<T> {
    fn heap_size_of_children(&self) -> usize {
        heap_size_of(self.as_ptr() as *const c_void) +
            self.iter().fold(0, |n, elem| n + elem.heap_size_of_children())
    }
}

impl<T> HeapSizeOf for Vec<Rc<T>> {
    fn heap_size_of_children(&self) -> usize {
        // The fate of measuring Rc<T> is still undecided, but we still want to measure
        // the space used for storing them.
        heap_size_of(self.as_ptr() as *const c_void)
    }
}

impl<K: HeapSizeOf, V: HeapSizeOf, S> HeapSizeOf for HashMap<K, V, S>
    where K: Eq + Hash, S: hash_state::HashState {
    fn heap_size_of_children(&self) -> usize {
        //TODO(#6908) measure actual bucket memory usage instead of approximating
        let size = self.capacity() * (size_of::<V>() + size_of::<K>());
        self.iter().fold(size, |n, (key, value)| {
            n + key.heap_size_of_children() + value.heap_size_of_children()
        })
    }
}

// FIXME(njn): We can't implement HeapSizeOf accurately for LinkedList because it requires access
// to the private Node type. Eventually we'll want to add HeapSizeOf (or equivalent) to Rust
// itself. In the meantime, we use the dirty hack of transmuting LinkedList into an identical type
// (LinkedList2) and measuring that.
impl<T: HeapSizeOf> HeapSizeOf for LinkedList<T> {
    fn heap_size_of_children(&self) -> usize {
        let list2: &LinkedList2<T> = unsafe { transmute(self) };
        list2.heap_size_of_children()
    }
}

struct LinkedList2<T> {
    _length: usize,
    list_head: Link<T>,
    _list_tail: Rawlink<Node<T>>,
}

type Link<T> = Option<Box<Node<T>>>;

struct Rawlink<T> {
    _p: *mut T,
}

struct Node<T> {
    next: Link<T>,
    _prev: Rawlink<Node<T>>,
    value: T,
}

impl<T: HeapSizeOf> HeapSizeOf for Node<T> {
    // Unlike most heap_size_of_children() functions, this one does *not* measure descendents.
    // Instead, LinkedList2<T>::heap_size_of_children() handles that, so that it can use iteration
    // instead of recursion, which avoids potentially blowing the stack.
    fn heap_size_of_children(&self) -> usize {
        self.value.heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for LinkedList2<T> {
    fn heap_size_of_children(&self) -> usize {
        let mut size = 0;
        let mut curr: &Link<T> = &self.list_head;
        while curr.is_some() {
            size += (*curr).heap_size_of_children();
            curr = &curr.as_ref().unwrap().next;
        }
        size
    }
}

// This is a basic sanity check. If the representation of LinkedList changes such that it becomes a
// different size to LinkedList2, this will fail at compile-time.
#[allow(dead_code)]
unsafe fn linked_list2_check() {
    transmute::<LinkedList<i32>, LinkedList2<i32>>(panic!());
}

// Currently, types that implement the Drop type are larger than those that don't. Because
// LinkedList implements Drop, LinkedList2 must also so that linked_list2_check() doesn't fail.
impl<T> Drop for LinkedList2<T> {
    fn drop(&mut self) {}
}

/// For use on types defined in external crates
/// with known heap sizes.
#[macro_export]
macro_rules! known_heap_size(
    ($size:expr, $($ty:ident),+) => (
        $(
            impl $crate::mem::HeapSizeOf for $ty {
                #[inline(always)]
                fn heap_size_of_children(&self) -> usize {
                    $size
                }
            }
        )+
    );
    ($size: expr, $($ty:ident<$($gen:ident),+>),+) => (
        $(
        impl<$($gen: $crate::mem::HeapSizeOf),+> $crate::mem::HeapSizeOf for $ty<$($gen),+> {
            #[inline(always)]
            fn heap_size_of_children(&self) -> usize {
                $size
            }
        }
        )+
    );
);

known_heap_size!(0, u8, u16, u32, u64, usize);
known_heap_size!(0, i8, i16, i32, i64, isize);
known_heap_size!(0, bool, f32, f64);

known_heap_size!(0, Rect<T>, Point2D<T>, Size2D<T>, Matrix2D<T>, SideOffsets2D<T>, Range<T>);
known_heap_size!(0, Length<T, U>, ScaleFactor<T, U, V>);

known_heap_size!(0, Au, WritingMode, CSSParserColor, Color, RGBA, Cursor, Matrix4, Atom);
known_heap_size!(0, JSVal, PagePx, ViewportPx, DevicePixel, QuirksMode);

// This is measured properly by the heap measurement implemented in SpiderMonkey.
impl<T: Copy + GCMethods<T>> HeapSizeOf for Heap<T> {
    fn heap_size_of_children(&self) -> usize {
        0
    }
}
<commit_msg>Auto merge of #6950 - servo:struct-pattern, r=glennw<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/. */

//! Data structure measurement.

use libc::{c_void, size_t};
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, LinkedList, hash_state};
use std::hash::Hash;
use std::mem::{size_of, transmute};
use std::sync::Arc;
use std::rc::Rc;

use azure::azure_hl::Color;
use cssparser::Color as CSSParserColor;
use cssparser::RGBA;
use cursor::Cursor;
use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D, Matrix4};
use euclid::length::Length;
use euclid::scale_factor::ScaleFactor;
use geometry::{PagePx, ViewportPx, Au};
use html5ever::tree_builder::QuirksMode;
use layers::geometry::DevicePixel;
use js::jsapi::Heap;
use js::rust::GCMethods;
use js::jsval::JSVal;
use logical_geometry::WritingMode;
use range::Range;
use string_cache::atom::Atom;
use url;

extern {
    // Get the size of a heap block.
    //
    // Ideally Rust would expose a function like this in std::rt::heap, which would avoid the
    // jemalloc dependence.
    //
    // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some
    // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice
    // this function doesn't modify the contents of the block that `ptr` points to, so we use
    // `*const c_void` here.
    fn je_malloc_usable_size(ptr: *const c_void) -> size_t;
}

// A wrapper for je_malloc_usable_size that handles `EMPTY` and returns `usize`.
pub fn heap_size_of(ptr: *const c_void) -> usize {
    if ptr == ::std::rt::heap::EMPTY as *const c_void {
        0
    } else {
        unsafe { je_malloc_usable_size(ptr) as usize }
    }
}

// The simplest trait for measuring the size of heap data structures. More complex traits that
// return multiple measurements -- e.g. measure text separately from images -- are also possible,
// and should be used when appropriate.
//
pub trait HeapSizeOf {
    /// Measure the size of any heap-allocated structures that hang off this value, but not the
    /// space taken up by the value itself (i.e. what size_of::<T> measures, more or less); that
    /// space is handled by the implementation of HeapSizeOf for Box<T> below.
    fn heap_size_of_children(&self) -> usize;
}

// There are two possible ways to measure the size of `self` when it's on the heap: compute it
// (with `::std::rt::heap::usable_size(::std::mem::size_of::<T>(), 0)`) or measure it directly
// using the heap allocator (with `heap_size_of`). We do the latter, for the following reasons.
//
// * The heap allocator is the true authority for the sizes of heap blocks; its measurement is
//   guaranteed to be correct. In comparison, size computations are error-prone. (For example, the
//   `rt::heap::usable_size` function used in some of Rust's non-default allocator implementations
//   underestimate the true usable size of heap blocks, which is safe in general but would cause
//   under-measurement here.)
//
// * If we measure something that isn't a heap block, we'll get a crash. This keeps us honest,
//   which is important because unsafe code is involved and this can be gotten wrong.
//
// However, in the best case, the two approaches should give the same results.
//
impl<T: HeapSizeOf> HeapSizeOf for Box<T> {
    fn heap_size_of_children(&self) -> usize {
        // Measure size of `self`.
        heap_size_of(&**self as *const T as *const c_void) + (**self).heap_size_of_children()
    }
}

impl HeapSizeOf for String {
    fn heap_size_of_children(&self) -> usize {
        heap_size_of(self.as_ptr() as *const c_void)
    }
}

impl<T: HeapSizeOf> HeapSizeOf for Option<T> {
    fn heap_size_of_children(&self) -> usize {
        match *self {
            None => 0,
            Some(ref x) => x.heap_size_of_children()
        }
    }
}

impl HeapSizeOf for url::Url {
    fn heap_size_of_children(&self) -> usize {
        // Using a struct pattern without `..` rather than `foo.bar` field access
        // makes sure this will be updated if a field is added.
        let &url::Url { ref scheme, ref scheme_data, ref query, ref fragment } = self;
        scheme.heap_size_of_children() +
        scheme_data.heap_size_of_children() +
        query.heap_size_of_children() +
        fragment.heap_size_of_children()
    }
}

impl HeapSizeOf for url::SchemeData {
    fn heap_size_of_children(&self) -> usize {
        match self {
            &url::SchemeData::Relative(ref data) => data.heap_size_of_children(),
            &url::SchemeData::NonRelative(ref str) => str.heap_size_of_children()
        }
    }
}

impl HeapSizeOf for url::RelativeSchemeData {
    fn heap_size_of_children(&self) -> usize {
        // Using a struct pattern without `..` rather than `foo.bar` field access
        // makes sure this will be updated if a field is added.
        let &url::RelativeSchemeData { ref username, ref password, ref host,
                                       ref port, ref default_port, ref path } = self;
        username.heap_size_of_children() +
        password.heap_size_of_children() +
        host.heap_size_of_children() +
        port.heap_size_of_children() +
        default_port.heap_size_of_children() +
        path.heap_size_of_children()
    }
}

impl HeapSizeOf for url::Host {
    fn heap_size_of_children(&self) -> usize {
        match self {
            &url::Host::Domain(ref str) => str.heap_size_of_children(),
            &url::Host::Ipv6(_) => 0
        }
    }
}

impl<T: HeapSizeOf, U: HeapSizeOf> HeapSizeOf for (T, U) {
    fn heap_size_of_children(&self) -> usize {
        self.0.heap_size_of_children() + self.1.heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for Arc<T> {
    fn heap_size_of_children(&self) -> usize {
        (**self).heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for RefCell<T> {
    fn heap_size_of_children(&self) -> usize {
        self.borrow().heap_size_of_children()
    }
}

impl<T: HeapSizeOf + Copy> HeapSizeOf for Cell<T> {
    fn heap_size_of_children(&self) -> usize {
        self.get().heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for Vec<T> {
    fn heap_size_of_children(&self) -> usize {
        heap_size_of(self.as_ptr() as *const c_void) +
            self.iter().fold(0, |n, elem| n + elem.heap_size_of_children())
    }
}

impl<T> HeapSizeOf for Vec<Rc<T>> {
    fn heap_size_of_children(&self) -> usize {
        // The fate of measuring Rc<T> is still undecided, but we still want to measure
        // the space used for storing them.
        heap_size_of(self.as_ptr() as *const c_void)
    }
}

impl<K: HeapSizeOf, V: HeapSizeOf, S> HeapSizeOf for HashMap<K, V, S>
    where K: Eq + Hash, S: hash_state::HashState {
    fn heap_size_of_children(&self) -> usize {
        //TODO(#6908) measure actual bucket memory usage instead of approximating
        let size = self.capacity() * (size_of::<V>() + size_of::<K>());
        self.iter().fold(size, |n, (key, value)| {
            n + key.heap_size_of_children() + value.heap_size_of_children()
        })
    }
}

// FIXME(njn): We can't implement HeapSizeOf accurately for LinkedList because it requires access
// to the private Node type. Eventually we'll want to add HeapSizeOf (or equivalent) to Rust
// itself. In the meantime, we use the dirty hack of transmuting LinkedList into an identical type
// (LinkedList2) and measuring that.
impl<T: HeapSizeOf> HeapSizeOf for LinkedList<T> {
    fn heap_size_of_children(&self) -> usize {
        let list2: &LinkedList2<T> = unsafe { transmute(self) };
        list2.heap_size_of_children()
    }
}

struct LinkedList2<T> {
    _length: usize,
    list_head: Link<T>,
    _list_tail: Rawlink<Node<T>>,
}

type Link<T> = Option<Box<Node<T>>>;

struct Rawlink<T> {
    _p: *mut T,
}

struct Node<T> {
    next: Link<T>,
    _prev: Rawlink<Node<T>>,
    value: T,
}

impl<T: HeapSizeOf> HeapSizeOf for Node<T> {
    // Unlike most heap_size_of_children() functions, this one does *not* measure descendents.
    // Instead, LinkedList2<T>::heap_size_of_children() handles that, so that it can use iteration
    // instead of recursion, which avoids potentially blowing the stack.
    fn heap_size_of_children(&self) -> usize {
        self.value.heap_size_of_children()
    }
}

impl<T: HeapSizeOf> HeapSizeOf for LinkedList2<T> {
    fn heap_size_of_children(&self) -> usize {
        let mut size = 0;
        let mut curr: &Link<T> = &self.list_head;
        while curr.is_some() {
            size += (*curr).heap_size_of_children();
            curr = &curr.as_ref().unwrap().next;
        }
        size
    }
}

// This is a basic sanity check. If the representation of LinkedList changes such that it becomes a
// different size to LinkedList2, this will fail at compile-time.
#[allow(dead_code)]
unsafe fn linked_list2_check() {
    transmute::<LinkedList<i32>, LinkedList2<i32>>(panic!());
}

// Currently, types that implement the Drop type are larger than those that don't. Because
// LinkedList implements Drop, LinkedList2 must also so that linked_list2_check() doesn't fail.
impl<T> Drop for LinkedList2<T> {
    fn drop(&mut self) {}
}

/// For use on types defined in external crates
/// with known heap sizes.
#[macro_export]
macro_rules! known_heap_size(
    ($size:expr, $($ty:ident),+) => (
        $(
            impl $crate::mem::HeapSizeOf for $ty {
                #[inline(always)]
                fn heap_size_of_children(&self) -> usize {
                    $size
                }
            }
        )+
    );
    ($size: expr, $($ty:ident<$($gen:ident),+>),+) => (
        $(
        impl<$($gen: $crate::mem::HeapSizeOf),+> $crate::mem::HeapSizeOf for $ty<$($gen),+> {
            #[inline(always)]
            fn heap_size_of_children(&self) -> usize {
                $size
            }
        }
        )+
    );
);

known_heap_size!(0, u8, u16, u32, u64, usize);
known_heap_size!(0, i8, i16, i32, i64, isize);
known_heap_size!(0, bool, f32, f64);

known_heap_size!(0, Rect<T>, Point2D<T>, Size2D<T>, Matrix2D<T>, SideOffsets2D<T>, Range<T>);
known_heap_size!(0, Length<T, U>, ScaleFactor<T, U, V>);

known_heap_size!(0, Au, WritingMode, CSSParserColor, Color, RGBA, Cursor, Matrix4, Atom);
known_heap_size!(0, JSVal, PagePx, ViewportPx, DevicePixel, QuirksMode);

// This is measured properly by the heap measurement implemented in SpiderMonkey.
impl<T: Copy + GCMethods<T>> HeapSizeOf for Heap<T> {
    fn heap_size_of_children(&self) -> usize {
        0
    }
}
<|endoftext|>