<commit_before>use criterion::*;
use ndarray::*;
use ndarray_linalg::*;

fn svd_small(c: &mut Criterion) {
    let mut group = c.benchmark_group("svd");
    for &n in &[4, 8, 16, 32, 64, 128] {
        group.bench_with_input(BenchmarkId::new("C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(false, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(false, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("u/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(true, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("u/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(true, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("vt/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("vt/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("uvt/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("uvt/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
    }
}

criterion_group!(svd, svd_small);
criterion_main!(svd);
<commit_msg>Add svddc<commit_after>use criterion::*;
use ndarray::*;
use ndarray_linalg::*;

fn svd_small(c: &mut Criterion) {
    let mut group = c.benchmark_group("svd");
    for &n in &[4, 8, 16, 32, 64, 128] {
        group.bench_with_input(BenchmarkId::new("C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(false, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(false, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("u/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(true, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("u/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(true, false).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("vt/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("vt/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("uvt/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("uvt/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svd(false, true).unwrap();
            })
        });
    }
}

fn svddc_small(c: &mut Criterion) {
    let mut group = c.benchmark_group("svddc");
    for &n in &[4, 8, 16, 32, 64, 128] {
        group.bench_with_input(BenchmarkId::new("C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svddc(UVTFlag::None).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svddc(UVTFlag::None).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("some/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svddc(UVTFlag::Some).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("some/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svddc(UVTFlag::Some).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("full/C", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n));
            b.iter(|| {
                let _ = a.svddc(UVTFlag::Full).unwrap();
            })
        });
        group.bench_with_input(BenchmarkId::new("full/F", n), &n, |b, n| {
            let a: Array2<f64> = random((*n, *n).f());
            b.iter(|| {
                let _ = a.svddc(UVTFlag::Full).unwrap();
            })
        });
    }
}

criterion_group!(svd, svd_small, svddc_small);
criterion_main!(svd);
<|endoftext|>


<commit_before>extern crate data_encoding;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json as json;
extern crate tempdir;
extern crate tuf;
extern crate url;

use data_encoding::HEXLOWER;
use std::fs::{self, File, DirEntry};
use std::io::{self, Read};
use std::path::{PathBuf, Path};
use tempdir::TempDir;
use tuf::{Tuf, Config, Error, RemoteRepo};
use tuf::meta::{Key, KeyValue, KeyType};
use url::Url;

fn load_vector_meta() -> String {
    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("tuf-test-vectors")
        .join("tuf")
        .join("vector-meta.json");
    let mut file = File::open(path).expect("couldn't open vector meta");
    let mut buf = String::new();
    file.read_to_string(&mut buf).expect("couldn't read vector meta");
    buf
}

#[derive(Deserialize)]
struct VectorMeta {
    vectors: Vec<VectorMetaEntry>,
}

#[derive(Deserialize)]
struct VectorMetaEntry {
    repo: String,
    error: Option<String>,
    is_success: bool,
    root_keys: Vec<RootKeyData>,
}

#[derive(Deserialize)]
struct RootKeyData {
    path: String,
    #[serde(rename = "type")]
    typ: String,
}

enum TestType {
    File,
    Http
}

fn ensure_empty(path: &Path) {
    if !path.is_dir() {
        panic!("Path wasn't a dir: {:?}", path)
    }

    let res = fs::read_dir(path).expect("couldn't read dir").collect::<Vec<io::Result<DirEntry>>>();
    if !res.is_empty() {
        panic!("Temp dir not empty: {:?}", res)
    }
    if !res.iter().all(|x| x.is_ok()) {
        panic!("Temp dir errors: {:?}", res)
    }
}

fn run_test_vector(test_path: &str, test_type: TestType) {
    let temp_dir = TempDir::new("rust-tuf").expect("couldn't make temp dir");
    let temp_path = temp_dir.into_path();

    println!("Temp dir is: {:?}", temp_path);

    let vector_meta: VectorMeta = json::from_str(&load_vector_meta())
        .expect("couldn't deserializd meta");

    let test_vector = vector_meta.vectors
        .iter()
        .filter(|v| v.repo == test_path)
        .collect::<Vec<&VectorMetaEntry>>()
        .pop()
        .expect(format!("No repo named {}", test_path).as_str());

    let vector_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("tuf-test-vectors")
        .join("tuf")
        .join(test_vector.repo.clone());

    println!("The test vector path is: {}",
             vector_path.to_string_lossy().into_owned());

    let root_keys = test_vector.root_keys
        .iter()
        .map(|k| {
            let file_path = vector_path.join("keys").join(k.path.clone());
            let mut file = File::open(file_path).expect("couldn't open file");
            let mut key = String::new();
            file.read_to_string(&mut key).expect("couldn't read key");

            match k.typ.as_ref() {
                "ed25519" => {
                    let val = HEXLOWER.decode(key.replace("\n", "").as_ref())
                        .expect("key value not hex");
                    Key {
                        typ: KeyType::Ed25519,
                        value: KeyValue(val),
                    }
                }
                x => panic!("unknown key type: {}", x),
            }
        })
        .collect();

    let config = match test_type {
        TestType::File => Config::build()
            .remote(RemoteRepo::File(vector_path.join("repo"))),
        TestType::Http => Config::build()
            .remote(RemoteRepo::Http(Url::parse(
                        &format!("http://localhost:8080/{}/repo", test_path)).expect("bad url"))),
    }.local_path(temp_path.clone())
        .finish()
        .expect("bad config");

    match (Tuf::from_root_keys(root_keys, config), &test_vector.error) {
        (Ok(ref tuf), &None) => {
            // first time pulls remote
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()), Ok(()));
            assert!(temp_path.join("targets").join("targets").join("file.txt").exists());
            // second time pulls local
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()), Ok(()));
        }

        (Ok(ref tuf), &Some(ref err)) if err == &"TargetHashMismatch".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()),
                       Err(Error::UnavailableTarget));
        }

        (Ok(ref tuf), &Some(ref err)) if err == &"OversizedTarget".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()),
                       Err(Error::UnavailableTarget));
        }

        (Err(Error::ExpiredMetadata(ref role)), &Some(ref err))
            if err.starts_with("ExpiredMetadata::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::UnmetThreshold(ref role)), &Some(ref err))
            if err.starts_with("UnmetThreshold::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::MetadataHashMismatch(ref role)), &Some(ref err))
            if err.starts_with("MetadataHashMismatch::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::OversizedMetadata(ref role)), &Some(ref err))
            if err.starts_with("OversizedMetadata::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        // we're using a json error because the threshold is checked in the deserializer
        // this may need to change in the future
        (Err(Error::Json(ref msg)), &Some(ref err)) if err.starts_with("IllegalThreshold::") => {
            let role = err.split("::").last().unwrap();

            assert!(msg.contains("threshold"),
                    format!("Role: {}, err: {}", role, err));
            assert!(err.to_lowercase()
                        .contains(role.to_lowercase().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::NonUniqueSignatures(ref role)), &Some(ref err)) if err.starts_with("NonUniqueSignatures::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Ok(ref tuf), &Some(ref err)) if err == &"UnavailableTarget".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()),
                       Err(Error::UnavailableTarget));
        }

        (Ok(ref tuf), &Some(ref err))
            if err == &"UnmetThreshold::Delegation".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()), Err(Error::UnavailableTarget));
        }

        x => panic!("Unexpected failures: {:?}", x),
    }
    ensure_empty(&temp_path.join("temp"));
    if !test_vector.is_success {
        ensure_empty(&temp_path.join("targets"))
    }
}

macro_rules! test_cases {
    ($name: expr, $md: ident) => {
        mod $md {
            use $crate::{run_test_vector, TestType};

            #[test]
            fn file_test() {
                run_test_vector($name, TestType::File)
            }

            // TODO no idea how windows shell scipting works
            #[cfg(not(windows))]
            #[test]
            fn http_test() {
                run_test_vector($name, TestType::Http)
            }
        }
    }
}

test_cases!("001", _001);
test_cases!("002", _002);
// test_cases!("003", _003);
// test_cases!("004", _004);
test_cases!("005", _005);
// test_cases!("006", _006);
test_cases!("007", _007);
test_cases!("008", _008);
test_cases!("009", _009);
test_cases!("010", _010);
test_cases!("011", _011);
test_cases!("012", _012);
test_cases!("013", _013);
test_cases!("014", _014);
test_cases!("015", _015);
test_cases!("016", _016);
test_cases!("017", _017);
test_cases!("018", _018);
test_cases!("019", _019);
test_cases!("020", _020);
test_cases!("021", _021);
test_cases!("022", _022);
test_cases!("023", _023);
test_cases!("024", _024);
test_cases!("025", _025);
test_cases!("026", _026);
// test_cases!("027", _027);
// test_cases!("028", _028);
test_cases!("029", _029);
test_cases!("030", _030);
test_cases!("031", _031);
test_cases!("032", _032);
test_cases!("033", _033);
test_cases!("034", _034);
// test_cases!("035", _035);
// test_cases!("036", _036);
test_cases!("037", _037);
test_cases!("038", _038);
test_cases!("039", _039);
test_cases!("040", _040);
// test_cases!("041", _041);
// test_cases!("042", _042);
// test_cases!("043", _043);
// test_cases!("044", _044);
test_cases!("045", _045);
test_cases!("046", _046);
test_cases!("047", _047);
test_cases!("048", _048);
test_cases!("049", _049);
test_cases!("050", _050);
test_cases!("051", _051);
test_cases!("052", _052);
test_cases!("053", _053);
test_cases!("054", _054);
test_cases!("055", _055);
<commit_msg>test unpinned repos<commit_after>extern crate data_encoding;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json as json;
extern crate tempdir;
extern crate tuf;
extern crate url;

use data_encoding::HEXLOWER;
use std::fs::{self, File, DirEntry};
use std::io::{self, Read};
use std::path::{PathBuf, Path};
use tempdir::TempDir;
use tuf::{Tuf, Config, Error, RemoteRepo};
use tuf::meta::{Key, KeyValue, KeyType};
use url::Url;

fn load_vector_meta() -> String {
    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("tuf-test-vectors")
        .join("tuf")
        .join("vector-meta.json");
    let mut file = File::open(path).expect("couldn't open vector meta");
    let mut buf = String::new();
    file.read_to_string(&mut buf).expect("couldn't read vector meta");
    buf
}

#[derive(Deserialize)]
struct VectorMeta {
    vectors: Vec<VectorMetaEntry>,
}

#[derive(Deserialize)]
struct VectorMetaEntry {
    repo: String,
    error: Option<String>,
    is_success: bool,
    root_keys: Vec<RootKeyData>,
}

#[derive(Deserialize)]
struct RootKeyData {
    path: String,
    #[serde(rename = "type")]
    typ: String,
}

enum TestType {
    File,
    Http
}

fn ensure_empty(path: &Path) {
    if !path.is_dir() {
        panic!("Path wasn't a dir: {:?}", path)
    }

    let res = fs::read_dir(path).expect("couldn't read dir").collect::<Vec<io::Result<DirEntry>>>();
    if !res.is_empty() {
        panic!("Temp dir not empty: {:?}", res)
    }
    if !res.iter().all(|x| x.is_ok()) {
        panic!("Temp dir errors: {:?}", res)
    }
}

fn run_test_vector(test_path: &str, test_type: TestType, pin_root_keys: bool) {
    let temp_dir = TempDir::new("rust-tuf").expect("couldn't make temp dir");
    let temp_path = temp_dir.into_path();

    println!("Temp dir is: {:?}", temp_path);

    let vector_meta: VectorMeta = json::from_str(&load_vector_meta())
        .expect("couldn't deserializd meta");

    let test_vector = vector_meta.vectors
        .iter()
        .filter(|v| v.repo == test_path)
        .collect::<Vec<&VectorMetaEntry>>()
        .pop()
        .expect(format!("No repo named {}", test_path).as_str());

    let vector_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("tests")
        .join("tuf-test-vectors")
        .join("tuf")
        .join(test_vector.repo.clone());

    println!("The test vector path is: {}",
             vector_path.to_string_lossy().into_owned());

    let config = match test_type {
        TestType::File => Config::build()
            .remote(RemoteRepo::File(vector_path.join("repo"))),
        TestType::Http => Config::build()
            .remote(RemoteRepo::Http(Url::parse(
                        &format!("http://localhost:8080/{}/repo", test_path)).expect("bad url"))),
    }.local_path(temp_path.clone())
        .finish()
        .expect("bad config");

    let tuf = if pin_root_keys {
        let root_keys = test_vector.root_keys
            .iter()
            .map(|k| {
                let file_path = vector_path.join("keys").join(k.path.clone());
                let mut file = File::open(file_path).expect("couldn't open file");
                let mut key = String::new();
                file.read_to_string(&mut key).expect("couldn't read key");

                match k.typ.as_ref() {
                    "ed25519" => {
                        let val = HEXLOWER.decode(key.replace("\n", "").as_ref())
                            .expect("key value not hex");
                        Key {
                            typ: KeyType::Ed25519,
                            value: KeyValue(val),
                        }
                    }
                    x => panic!("unknown key type: {}", x),
                }
            })
            .collect();
        Tuf::from_root_keys(root_keys, config)
    } else {
        Tuf::initialize(&temp_path)
            .expect("failed to initialize");
        fs::copy(vector_path.join("repo").join("1.root.json"),
                 temp_path.join("metadata").join("current").join("root.json"))
            .expect("failed to copy root.json");
        Tuf::new(config)
    };

    match (tuf, &test_vector.error) {
        (Ok(ref tuf), &None) => {
            // first time pulls remote
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()), Ok(()));
            assert!(temp_path.join("targets").join("targets").join("file.txt").exists());
            // second time pulls local
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()), Ok(()));
        }

        (Ok(ref tuf), &Some(ref err)) if err == &"TargetHashMismatch".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()),
                       Err(Error::UnavailableTarget));
        }

        (Ok(ref tuf), &Some(ref err)) if err == &"OversizedTarget".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()),
                       Err(Error::UnavailableTarget));
        }

        (Err(Error::ExpiredMetadata(ref role)), &Some(ref err))
            if err.starts_with("ExpiredMetadata::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::UnmetThreshold(ref role)), &Some(ref err))
            if err.starts_with("UnmetThreshold::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::MetadataHashMismatch(ref role)), &Some(ref err))
            if err.starts_with("MetadataHashMismatch::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::OversizedMetadata(ref role)), &Some(ref err))
            if err.starts_with("OversizedMetadata::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        // we're using a json error because the threshold is checked in the deserializer
        // this may need to change in the future
        (Err(Error::Json(ref msg)), &Some(ref err)) if err.starts_with("IllegalThreshold::") => {
            let role = err.split("::").last().unwrap();

            assert!(msg.contains("threshold"),
                    format!("Role: {}, err: {}", role, err));
            assert!(err.to_lowercase()
                        .contains(role.to_lowercase().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Err(Error::NonUniqueSignatures(ref role)), &Some(ref err)) if err.starts_with("NonUniqueSignatures::") => {
            assert!(err.to_lowercase()
                        .ends_with(role.to_string().as_str()),
                    format!("Role: {}, err: {}", role, err))
        }

        (Ok(ref tuf), &Some(ref err)) if err == &"UnavailableTarget".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()),
                       Err(Error::UnavailableTarget));
        }

        (Ok(ref tuf), &Some(ref err))
            if err == &"UnmetThreshold::Delegation".to_string() => {
            assert_eq!(tuf.fetch_target("targets/file.txt").map(|_| ()), Err(Error::UnavailableTarget));
        }

        x => panic!("Unexpected failures: {:?}", x),
    }
    ensure_empty(&temp_path.join("temp"));
    if !test_vector.is_success {
        ensure_empty(&temp_path.join("targets"))
    }
}

macro_rules! test_cases {
    ($name: expr, $md: ident) => {
        mod $md {
            use $crate::{run_test_vector, TestType};

            #[test]
            fn file_pinned() {
                run_test_vector($name, TestType::File, true)
            }

            #[test]
            fn file_unpinned() {
                run_test_vector($name, TestType::File, false)
            }

            // TODO no idea how windows shell scipting works
            #[cfg(not(windows))]
            #[test]
            fn http_pinned() {
                run_test_vector($name, TestType::Http, true)
            }

            // TODO no idea how windows shell scipting works
            #[cfg(not(windows))]
            #[test]
            fn http_unpinned() {
                run_test_vector($name, TestType::Http, false)
            }
        }
    }
}

test_cases!("001", _001);
test_cases!("002", _002);
// test_cases!("003", _003);
// test_cases!("004", _004);
test_cases!("005", _005);
// test_cases!("006", _006);
test_cases!("007", _007);
test_cases!("008", _008);
test_cases!("009", _009);
test_cases!("010", _010);
test_cases!("011", _011);
test_cases!("012", _012);
test_cases!("013", _013);
test_cases!("014", _014);
test_cases!("015", _015);
test_cases!("016", _016);
test_cases!("017", _017);
test_cases!("018", _018);
test_cases!("019", _019);
test_cases!("020", _020);
test_cases!("021", _021);
test_cases!("022", _022);
test_cases!("023", _023);
test_cases!("024", _024);
test_cases!("025", _025);
test_cases!("026", _026);
// test_cases!("027", _027);
// test_cases!("028", _028);
test_cases!("029", _029);
test_cases!("030", _030);
test_cases!("031", _031);
test_cases!("032", _032);
test_cases!("033", _033);
test_cases!("034", _034);
// test_cases!("035", _035);
// test_cases!("036", _036);
test_cases!("037", _037);
test_cases!("038", _038);
test_cases!("039", _039);
test_cases!("040", _040);
// test_cases!("041", _041);
// test_cases!("042", _042);
// test_cases!("043", _043);
// test_cases!("044", _044);
test_cases!("045", _045);
test_cases!("046", _046);
test_cases!("047", _047);
test_cases!("048", _048);
test_cases!("049", _049);
test_cases!("050", _050);
test_cases!("051", _051);
test_cases!("052", _052);
test_cases!("053", _053);
test_cases!("054", _054);
test_cases!("055", _055);
<|endoftext|>


<commit_before>#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate serde_json;
extern crate tokio_core;
extern crate url;
extern crate telegram_bot_raw;

use std::sync::Arc;
use std::cmp::max;
use std::collections::VecDeque;

use futures::{Future, Stream, Poll, Async};
use futures::future::{result};
use hyper::{Body, Method};
use hyper::client::Client;
use hyper::header::ContentType;
use hyper_tls::HttpsConnector;
use tokio_core::reactor::Handle;
use url::Url;

use telegram_bot_raw::{GetUpdates, Request, Response, ResponseParameters, Update, Integer};

const TELEGRAM_URL: &'static str = "https://api.telegram.org/";
const TELEGRAM_LONG_POLL_TIMEOUT: usize = 5;

error_chain! {
    foreign_links {
        Url(url::ParseError);
        Hyper(hyper::Error);
        Json(serde_json::Error);
    }

    errors {
        TelegramError {
            description: String,
            parameters: Option<ResponseParameters>
        }
    }
}

#[must_use = "futures do nothing unless polled"]
pub struct TelegramFuture<T> {
    inner: Box<Future<Item=T, Error=Error>>
}

impl<T> Future for TelegramFuture<T> {
    type Item = T;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.inner.poll()
    }
}

#[must_use = "streams do nothing unless polled"]
pub struct UpdatesStream {
    bot: Bot,
    last_update: Integer,
    buffer: VecDeque<Update>,
    current_request: Option<TelegramFuture<Vec<Update>>>,
    timeout: usize,
}

impl Stream for UpdatesStream {
    type Item = Update;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(value) = self.buffer.pop_front() {
            return Ok(Async::Ready(Some(value)))
        }

        let result = match self.current_request {
            None => None,
            Some(ref mut current_request) => {
                let polled_update = current_request.poll();
                match polled_update {
                    Ok(Async::NotReady) => return Ok(Async::NotReady),
                    Ok(Async::Ready(updates)) => {
                        if updates.is_empty() {
                            None
                        } else {
                            for update in updates.iter() {
                                self.last_update = max(update.id, self.last_update);
                            }
                            Some(updates)
                        }
                    },
                    Err(err) => return Err(err)
                }
            }
        };

        match result {
            None => {
                let request = self.bot.send(GetUpdates {
                    offset: Some(self.last_update + 1),
                    limit: None,
                    timeout: Some(self.timeout as Integer),
                    allowed_updates: Vec::new(),
                });

                self.current_request = Some(request);
                self.poll()
            },
            Some(mut updates) => {
                self.current_request = None;
                // Updates are guarantied to be not empty
                self.buffer.extend(updates.drain(1..));
                let result = updates.pop().unwrap();
                return Ok(Async::Ready(Some(result)))
            }
        }
    }
}

impl UpdatesStream {
    fn new(bot: &Bot) -> Self {
        UpdatesStream {
            bot: bot.clone(),
            last_update: 0,
            buffer: VecDeque::new(),
            current_request: None,
            timeout: TELEGRAM_LONG_POLL_TIMEOUT,
        }
    }
}

#[derive(Debug, Clone)]
pub struct Bot {
    inner: Arc<BotInner>,
}

#[derive(Debug, Clone)]
struct BotInner {
    token: String,
    client: Client<HttpsConnector>,
}

impl Bot {
    pub fn from_token(handle: &Handle, token: &str) -> Result<Self> {
        let connector = HttpsConnector::new(1, handle);
        let config = Client::configure().connector(connector);

        Ok(Bot {
            inner: Arc::new(BotInner {
                token: token.to_string(),
                client: config.build(handle),
            }),
        })
    }

    pub fn stream(&self) -> UpdatesStream {
        UpdatesStream::new(self)
    }

    pub fn send<Req>(&self, request: Req) -> TelegramFuture<Req::Response>
        where Req: Request + 'static, <Req as Request>::Response: std::marker::Send + 'static {

        let url = result(url(&self.inner.token, request.name()));
        let body = futures::lazy(move || {
            serde_json::to_vec(&request).map(Body::from)
        }).map_err(From::from);

        let bot = self.clone();
        let response = url.join(body).and_then(move |(url, body)| {
            let mut http_request = hyper::client::Request::new(Method::Post, url);
            http_request.set_body(body);
            http_request.headers_mut().set(ContentType::json());

            bot.inner.client.request(http_request).map_err(From::from)
        });

        let bytes = response.and_then(|response| {
            response.body().map_err(From::from)
                .fold(vec![], |mut result, chunk| -> Result<Vec<u8>> {
                    result.extend_from_slice(&chunk);
                    Ok(result)
            })
        });

        let future = bytes.and_then(|bytes| {
            result(serde_json::from_slice(&bytes).map_err(From::from).and_then(|value| {
                match value {
                    Response::Success {result} => Ok(result),
                    Response::Error { description, parameters } => {
                        Err(ErrorKind::TelegramError {
                            description: description,
                            parameters: parameters
                        }.into())
                    },
                }
            }))
        });

        TelegramFuture {
            inner: Box::new(future)
        }
    }
}

fn url(token: &str, method: &str) -> Result<Url> {
    Url::parse(&format!("{}bot{}/{}", TELEGRAM_URL, token, method)).map_err(From::from)
}
<commit_msg>feat: add Bot::spawn method<commit_after>#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate serde_json;
extern crate tokio_core;
extern crate url;
extern crate telegram_bot_raw;

use std::sync::Arc;
use std::cmp::max;
use std::collections::VecDeque;

use futures::{Future, Stream, Poll, Async};
use futures::future::{result};
use hyper::{Body, Method};
use hyper::client::Client;
use hyper::header::ContentType;
use hyper_tls::HttpsConnector;
use tokio_core::reactor::Handle;
use url::Url;

use telegram_bot_raw::{GetUpdates, Request, Response, ResponseParameters, Update, Integer};

const TELEGRAM_URL: &'static str = "https://api.telegram.org/";
const TELEGRAM_LONG_POLL_TIMEOUT: usize = 5;

error_chain! {
    foreign_links {
        Url(url::ParseError);
        Hyper(hyper::Error);
        Json(serde_json::Error);
    }

    errors {
        TelegramError {
            description: String,
            parameters: Option<ResponseParameters>
        }
    }
}

#[must_use = "futures do nothing unless polled"]
pub struct TelegramFuture<T> {
    inner: Box<Future<Item=T, Error=Error>>
}

impl<T> Future for TelegramFuture<T> {
    type Item = T;
    type Error = Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.inner.poll()
    }
}

#[must_use = "streams do nothing unless polled"]
pub struct UpdatesStream {
    bot: Bot,
    last_update: Integer,
    buffer: VecDeque<Update>,
    current_request: Option<TelegramFuture<Vec<Update>>>,
    timeout: usize,
}

impl Stream for UpdatesStream {
    type Item = Update;
    type Error = Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        if let Some(value) = self.buffer.pop_front() {
            return Ok(Async::Ready(Some(value)))
        }

        let result = match self.current_request {
            None => None,
            Some(ref mut current_request) => {
                let polled_update = current_request.poll();
                match polled_update {
                    Ok(Async::NotReady) => return Ok(Async::NotReady),
                    Ok(Async::Ready(updates)) => {
                        if updates.is_empty() {
                            None
                        } else {
                            for update in updates.iter() {
                                self.last_update = max(update.id, self.last_update);
                            }
                            Some(updates)
                        }
                    },
                    Err(err) => return Err(err)
                }
            }
        };

        match result {
            None => {
                let request = self.bot.send(GetUpdates {
                    offset: Some(self.last_update + 1),
                    limit: None,
                    timeout: Some(self.timeout as Integer),
                    allowed_updates: Vec::new(),
                });

                self.current_request = Some(request);
                self.poll()
            },
            Some(mut updates) => {
                self.current_request = None;
                // Updates are guarantied to be not empty
                self.buffer.extend(updates.drain(1..));
                let result = updates.pop().unwrap();
                return Ok(Async::Ready(Some(result)))
            }
        }
    }
}

impl UpdatesStream {
    fn new(bot: &Bot) -> Self {
        UpdatesStream {
            bot: bot.clone(),
            last_update: 0,
            buffer: VecDeque::new(),
            current_request: None,
            timeout: TELEGRAM_LONG_POLL_TIMEOUT,
        }
    }
}

#[derive(Clone)]
pub struct Bot {
    inner: Arc<BotInner>,
}

#[derive(Clone)]
struct BotInner {
    token: String,
    client: Client<HttpsConnector>,
    handle: Handle,
}

impl Bot {
    pub fn from_token(handle: &Handle, token: &str) -> Result<Self> {
        let connector = HttpsConnector::new(1, handle);
        let config = Client::configure().connector(connector);

        Ok(Bot {
            inner: Arc::new(BotInner {
                token: token.to_string(),
                client: config.build(handle),
                handle: handle.clone(),
            }),
        })
    }

    pub fn stream(&self) -> UpdatesStream {
        UpdatesStream::new(self)
    }

    pub fn spawn<Req>(&self, request: Req)
        where Req: Request + 'static, <Req as Request>::Response: std::marker::Send + 'static {

        self.inner.handle.spawn(self.send(request).then(|_| Ok(())))
    }

    pub fn send<Req>(&self, request: Req) -> TelegramFuture<Req::Response>
        where Req: Request + 'static, <Req as Request>::Response: std::marker::Send + 'static {

        let url = result(url(&self.inner.token, request.name()));
        let body = futures::lazy(move || {
            serde_json::to_vec(&request).map(Body::from)
        }).map_err(From::from);

        let bot = self.clone();
        let response = url.join(body).and_then(move |(url, body)| {
            let mut http_request = hyper::client::Request::new(Method::Post, url);
            http_request.set_body(body);
            http_request.headers_mut().set(ContentType::json());

            bot.inner.client.request(http_request).map_err(From::from)
        });

        let bytes = response.and_then(|response| {
            response.body().map_err(From::from)
                .fold(vec![], |mut result, chunk| -> Result<Vec<u8>> {
                    result.extend_from_slice(&chunk);
                    Ok(result)
            })
        });

        let future = bytes.and_then(|bytes| {
            result(serde_json::from_slice(&bytes).map_err(From::from).and_then(|value| {
                match value {
                    Response::Success {result} => Ok(result),
                    Response::Error { description, parameters } => {
                        Err(ErrorKind::TelegramError {
                            description: description,
                            parameters: parameters
                        }.into())
                    },
                }
            }))
        });

        TelegramFuture {
            inner: Box::new(future)
        }
    }
}

fn url(token: &str, method: &str) -> Result<Url> {
    Url::parse(&format!("{}bot{}/{}", TELEGRAM_URL, token, method)).map_err(From::from)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use std::vec::Vec;
use libc;
use capability::ClientHook;
use common::*;
use message;

pub use self::FirstSegment::{NumWords, ZeroedWords};

pub type SegmentId = u32;

#[deriving(Copy)]
pub struct SegmentReader {
    pub arena : ArenaPtr,
    pub ptr : *const Word,
    pub size : WordCount32
}

unsafe impl Send for SegmentReader {}

impl SegmentReader {

    #[inline]
    pub unsafe fn get_start_ptr(&self) -> *const Word {
        self.ptr.offset(0)
    }

    #[inline]
    pub fn contains_interval(&self, from : *const Word, to : *const Word) -> bool {
        let this_begin : uint = self.ptr.to_uint();
        let this_end : uint = unsafe { self.ptr.offset(self.size as int).to_uint() };
        return from.to_uint() >= this_begin && to.to_uint() <= this_end && from.to_uint() <= to.to_uint();
        // TODO readLimiter
    }
}

#[deriving(Copy)]
pub struct SegmentBuilder {
    pub reader : SegmentReader,
    pub id : SegmentId,
    pos : *mut Word,
}

unsafe impl Send for SegmentBuilder {}

impl SegmentBuilder {

    pub fn new(arena : *mut BuilderArena,
               id : SegmentId,
               ptr : *mut Word,
               size : WordCount32) -> SegmentBuilder {
        SegmentBuilder {
            reader : SegmentReader {
                arena : ArenaPtr::Builder(arena),
                ptr : unsafe {::std::mem::transmute(ptr)},
                size : size
            },
            id : id,
            pos : ptr,
        }
    }

    pub fn get_word_offset_to(&mut self, ptr : *mut Word) -> WordCount32 {
        let this_addr : uint = self.reader.ptr.to_uint();
        let ptr_addr : uint = ptr.to_uint();
        assert!(ptr_addr >= this_addr);
        let result = (ptr_addr - this_addr) / BYTES_PER_WORD;
        return result as u32;
    }

    #[inline]
    pub fn current_size(&self) -> WordCount32 {
        ptr_sub(self.pos, self.reader.ptr) as u32
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> Option<*mut Word> {
        if amount > self.reader.size - self.current_size() {
            return None;
        } else {
            let result = self.pos;
            self.pos = unsafe { self.pos.offset(amount as int) };
            return Some(result);
        }
    }

    #[inline]
    pub fn get_ptr_unchecked(&self, offset : WordCount32) -> *mut Word {
        unsafe {
            ::std::mem::transmute(self.reader.ptr.offset(offset as int))
        }
    }

    #[inline]
    pub fn get_segment_id(&self) -> SegmentId { self.id }

    #[inline]
    pub fn get_arena(&self) -> *mut BuilderArena {
        match self.reader.arena {
            ArenaPtr::Builder(b) => b,
            _ => unreachable!()
        }
    }
}

pub struct ReaderArena {
    //    message : *message::MessageReader<'a>,
    pub segment0 : SegmentReader,

    pub more_segments : Vec<SegmentReader>,
    //XXX should this be a map as in capnproto-c++?

    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,

    pub fail_fast : bool,
}

impl ReaderArena {
    pub fn new(segments : &[&[Word]], options : message::ReaderOptions) -> Box<ReaderArena> {
        assert!(segments.len() > 0);
        let mut arena = box ReaderArena {
            segment0 : SegmentReader {
                arena : ArenaPtr::Null,
                ptr : unsafe { segments[0].unsafe_get(0) },
                size : segments[0].len() as u32
            },
            more_segments : Vec::new(),
            cap_table : Vec::new(),
            fail_fast : options.fail_fast,
        };

        let arena_ptr = ArenaPtr::Reader (&*arena);

        arena.segment0.arena = arena_ptr;

        if segments.len() > 1 {
            let mut more_segment_readers = Vec::new();
            for segment in segments.slice_from(1).iter() {
                let segment_reader = SegmentReader {
                    arena : arena_ptr,
                    ptr : unsafe { segment.unsafe_get(0) },
                    size : segment.len() as u32
                };
                more_segment_readers.push(segment_reader);
            }
            arena.more_segments = more_segment_readers;
        }

        arena
    }

    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        if id == 0 {
            return &self.segment0 as *const SegmentReader;
        } else {
            unsafe { self.more_segments.as_slice().unsafe_get(id as uint - 1) as *const SegmentReader }
        }
    }

    #[inline]
    pub fn init_cap_table(&mut self, cap_table : Vec<Option<Box<ClientHook+Send>>>) {
        self.cap_table = cap_table;
    }

}

pub struct BuilderArena {
    pub segment0 : SegmentBuilder,
    pub more_segments : Vec<Box<SegmentBuilder>>,
    pub allocation_strategy : message::AllocationStrategy,
    pub owned_memory : Vec<*mut Word>,
    pub next_size : u32,
    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,
    pub fail_fast : bool,
}

impl Drop for BuilderArena {
    fn drop(&mut self) {
        for &segment_ptr in self.owned_memory.iter() {
            unsafe { libc::free(::std::mem::transmute(segment_ptr)); }
        }
    }
}

pub enum FirstSegment<'a> {
    NumWords(u32),
    ZeroedWords(&'a mut [Word])
}

impl BuilderArena {

    pub fn new(allocation_strategy : message::AllocationStrategy,
               first_segment : FirstSegment,
               fail_fast : bool) -> Box<BuilderArena> {

        let (first_segment, num_words, owned_memory) : (*mut Word, u32, Vec<*mut Word>) = unsafe {
            match first_segment {
                NumWords(n) => {
                    let ptr = ::std::mem::transmute(
                        libc::calloc(n as libc::size_t,
                                          BYTES_PER_WORD as libc::size_t));
                    (ptr, n, vec!(ptr))
                }
                ZeroedWords(w) => (w.as_mut_ptr(), w.len() as u32, Vec::new())
            }};

        let mut result = box BuilderArena {
            segment0 : SegmentBuilder {
                reader : SegmentReader {
                    ptr : first_segment as *const Word,
                    size : num_words,
                    arena : ArenaPtr::Null },
                id : 0,
                pos : first_segment,
            },
            more_segments : Vec::new(),
            allocation_strategy : allocation_strategy,
            owned_memory : owned_memory,
            next_size : num_words,
            cap_table : Vec::new(),
            fail_fast : fail_fast,
        };

        let arena_ptr = { let ref mut ptr = *result; ptr as *mut BuilderArena};
        result.segment0.reader.arena = ArenaPtr::Builder(arena_ptr);

        result
    }

    pub fn allocate_owned_memory(&mut self, minimum_size : WordCount32) -> (*mut Word, WordCount32) {
        let size = ::std::cmp::max(minimum_size, self.next_size);
        let new_words : *mut Word = unsafe {
            ::std::mem::transmute(libc::calloc(size as libc::size_t,
                                                   BYTES_PER_WORD as libc::size_t)) };

        self.owned_memory.push(new_words);

        match self.allocation_strategy {
            message::AllocationStrategy::GrowHeuristically => { self.next_size += size; }
            _ => { }
        }
        (new_words, size)
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> (*mut SegmentBuilder, *mut Word) {
        unsafe {
            match self.segment0.allocate(amount) {
                Some(result) => { return ((&mut self.segment0) as *mut SegmentBuilder, result) }
                None => {}
            }

            //# Need to fall back to additional segments.

            let id = {
                let len = self.more_segments.len();
                if len == 0 { 1 }
                else {
                    let result_ptr = &mut *self.more_segments.as_mut_slice()[len-1] as *mut SegmentBuilder;
                    match self.more_segments.as_mut_slice()[len - 1].allocate(amount) {
                        Some(result) => { return (result_ptr, result) }
                        None => { len + 1 }
                    }
                }};

            let (words, size) = self.allocate_owned_memory(amount);
            let mut new_builder = box SegmentBuilder::new(self, id as u32, words, size);
            let builder_ptr = &mut *new_builder as *mut SegmentBuilder;

            self.more_segments.push(new_builder);

            (builder_ptr, (*builder_ptr).allocate(amount).unwrap() )
        }
    }

    pub fn get_segment(&mut self, id : SegmentId) -> *mut SegmentBuilder {
        if id == 0 {
            &mut self.segment0 as *mut SegmentBuilder
        } else {
            &mut *self.more_segments.as_mut_slice()[(id - 1) as uint] as *mut SegmentBuilder
        }
    }

    pub fn get_segments_for_output<T>(&self, cont : |&[&[Word]]| -> T) -> T {
        unsafe {
            if self.more_segments.len() == 0 {
                let v = ::std::slice::from_raw_buf::<Word>(
                    &self.segment0.reader.ptr,
                    self.segment0.current_size() as uint);
                cont(&[v])
            } else {
                let mut result = Vec::new();
                result.push(::std::mem::transmute(
                    ::std::raw::Slice { data : self.segment0.reader.ptr,
                                      len : self.segment0.current_size() as uint}));

                for seg in self.more_segments.iter() {
                    result.push(::std::mem::transmute(
                        ::std::raw::Slice { data : seg.reader.ptr,
                                            len : seg.current_size() as uint}));
                }
                cont(result.as_slice())
            }
        }
    }

    pub fn get_cap_table<'a>(&'a self) -> &'a [Option<Box<ClientHook+Send>>] {
        self.cap_table.as_slice()
    }

    pub fn inject_cap(&mut self, cap : Box<ClientHook+Send>) -> u32 {
        self.cap_table.push(Some(cap));
        self.cap_table.len() as u32 - 1
    }
}

#[deriving(Copy)]
pub enum ArenaPtr {
    Reader(*const ReaderArena),
    Builder(*mut BuilderArena),
    Null
}

impl ArenaPtr {
    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (&*reader).try_get_segment(id)
                }
                &ArenaPtr::Builder(builder) => {
                    if id == 0 {
                        &(*builder).segment0.reader as *const SegmentReader
                    } else {
                        &(*builder).more_segments.as_slice()[id as uint - 1].reader as *const SegmentReader
                    }
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

    pub fn extract_cap(&self, index : uint) -> Option<Box<ClientHook+Send>> {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    if index < (*reader).cap_table.len() {
                        match (*reader).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Builder(builder) => {
                    if index < (*builder).cap_table.len() {
                        match (*builder).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Null => {
                    panic!();
                }
            }
        }
    }

    pub fn fail_fast(&self) -> bool {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (*reader).fail_fast
                }
                &ArenaPtr::Builder(builder) => {
                    (*builder).fail_fast
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

}
<commit_msg>kill some warnings<commit_after>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use std::vec::Vec;
use libc;
use capability::ClientHook;
use common::*;
use message;

pub use self::FirstSegment::{NumWords, ZeroedWords};

pub type SegmentId = u32;

#[deriving(Copy)]
pub struct SegmentReader {
    pub arena : ArenaPtr,
    pub ptr : *const Word,
    pub size : WordCount32
}

unsafe impl Send for SegmentReader {}

impl SegmentReader {

    #[inline]
    pub unsafe fn get_start_ptr(&self) -> *const Word {
        self.ptr.offset(0)
    }

    #[inline]
    pub fn contains_interval(&self, from : *const Word, to : *const Word) -> bool {
        let this_begin : uint = self.ptr as uint;
        let this_end : uint = unsafe { self.ptr.offset(self.size as int) as uint };
        return from as uint >= this_begin && to as uint <= this_end && from as uint <= to as uint;
        // TODO readLimiter
    }
}

#[deriving(Copy)]
pub struct SegmentBuilder {
    pub reader : SegmentReader,
    pub id : SegmentId,
    pos : *mut Word,
}

unsafe impl Send for SegmentBuilder {}

impl SegmentBuilder {

    pub fn new(arena : *mut BuilderArena,
               id : SegmentId,
               ptr : *mut Word,
               size : WordCount32) -> SegmentBuilder {
        SegmentBuilder {
            reader : SegmentReader {
                arena : ArenaPtr::Builder(arena),
                ptr : unsafe {::std::mem::transmute(ptr)},
                size : size
            },
            id : id,
            pos : ptr,
        }
    }

    pub fn get_word_offset_to(&mut self, ptr : *mut Word) -> WordCount32 {
        let this_addr : uint = self.reader.ptr as uint;
        let ptr_addr : uint = ptr as uint;
        assert!(ptr_addr >= this_addr);
        let result = (ptr_addr - this_addr) / BYTES_PER_WORD;
        return result as u32;
    }

    #[inline]
    pub fn current_size(&self) -> WordCount32 {
        ptr_sub(self.pos, self.reader.ptr) as u32
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> Option<*mut Word> {
        if amount > self.reader.size - self.current_size() {
            return None;
        } else {
            let result = self.pos;
            self.pos = unsafe { self.pos.offset(amount as int) };
            return Some(result);
        }
    }

    #[inline]
    pub fn get_ptr_unchecked(&self, offset : WordCount32) -> *mut Word {
        unsafe {
            ::std::mem::transmute(self.reader.ptr.offset(offset as int))
        }
    }

    #[inline]
    pub fn get_segment_id(&self) -> SegmentId { self.id }

    #[inline]
    pub fn get_arena(&self) -> *mut BuilderArena {
        match self.reader.arena {
            ArenaPtr::Builder(b) => b,
            _ => unreachable!()
        }
    }
}

pub struct ReaderArena {
    //    message : *message::MessageReader<'a>,
    pub segment0 : SegmentReader,

    pub more_segments : Vec<SegmentReader>,
    //XXX should this be a map as in capnproto-c++?

    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,

    pub fail_fast : bool,
}

impl ReaderArena {
    pub fn new(segments : &[&[Word]], options : message::ReaderOptions) -> Box<ReaderArena> {
        assert!(segments.len() > 0);
        let mut arena = box ReaderArena {
            segment0 : SegmentReader {
                arena : ArenaPtr::Null,
                ptr : unsafe { segments[0].unsafe_get(0) },
                size : segments[0].len() as u32
            },
            more_segments : Vec::new(),
            cap_table : Vec::new(),
            fail_fast : options.fail_fast,
        };

        let arena_ptr = ArenaPtr::Reader (&*arena);

        arena.segment0.arena = arena_ptr;

        if segments.len() > 1 {
            let mut more_segment_readers = Vec::new();
            for segment in segments.slice_from(1).iter() {
                let segment_reader = SegmentReader {
                    arena : arena_ptr,
                    ptr : unsafe { segment.unsafe_get(0) },
                    size : segment.len() as u32
                };
                more_segment_readers.push(segment_reader);
            }
            arena.more_segments = more_segment_readers;
        }

        arena
    }

    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        if id == 0 {
            return &self.segment0 as *const SegmentReader;
        } else {
            unsafe { self.more_segments.as_slice().unsafe_get(id as uint - 1) as *const SegmentReader }
        }
    }

    #[inline]
    pub fn init_cap_table(&mut self, cap_table : Vec<Option<Box<ClientHook+Send>>>) {
        self.cap_table = cap_table;
    }

}

pub struct BuilderArena {
    pub segment0 : SegmentBuilder,
    pub more_segments : Vec<Box<SegmentBuilder>>,
    pub allocation_strategy : message::AllocationStrategy,
    pub owned_memory : Vec<*mut Word>,
    pub next_size : u32,
    pub cap_table : Vec<Option<Box<ClientHook+Send>>>,
    pub fail_fast : bool,
}

impl Drop for BuilderArena {
    fn drop(&mut self) {
        for &segment_ptr in self.owned_memory.iter() {
            unsafe { libc::free(::std::mem::transmute(segment_ptr)); }
        }
    }
}

pub enum FirstSegment<'a> {
    NumWords(u32),
    ZeroedWords(&'a mut [Word])
}

impl BuilderArena {

    pub fn new(allocation_strategy : message::AllocationStrategy,
               first_segment : FirstSegment,
               fail_fast : bool) -> Box<BuilderArena> {

        let (first_segment, num_words, owned_memory) : (*mut Word, u32, Vec<*mut Word>) = unsafe {
            match first_segment {
                NumWords(n) => {
                    let ptr = ::std::mem::transmute(
                        libc::calloc(n as libc::size_t,
                                          BYTES_PER_WORD as libc::size_t));
                    (ptr, n, vec!(ptr))
                }
                ZeroedWords(w) => (w.as_mut_ptr(), w.len() as u32, Vec::new())
            }};

        let mut result = box BuilderArena {
            segment0 : SegmentBuilder {
                reader : SegmentReader {
                    ptr : first_segment as *const Word,
                    size : num_words,
                    arena : ArenaPtr::Null },
                id : 0,
                pos : first_segment,
            },
            more_segments : Vec::new(),
            allocation_strategy : allocation_strategy,
            owned_memory : owned_memory,
            next_size : num_words,
            cap_table : Vec::new(),
            fail_fast : fail_fast,
        };

        let arena_ptr = { let ref mut ptr = *result; ptr as *mut BuilderArena};
        result.segment0.reader.arena = ArenaPtr::Builder(arena_ptr);

        result
    }

    pub fn allocate_owned_memory(&mut self, minimum_size : WordCount32) -> (*mut Word, WordCount32) {
        let size = ::std::cmp::max(minimum_size, self.next_size);
        let new_words : *mut Word = unsafe {
            ::std::mem::transmute(libc::calloc(size as libc::size_t,
                                                   BYTES_PER_WORD as libc::size_t)) };

        self.owned_memory.push(new_words);

        match self.allocation_strategy {
            message::AllocationStrategy::GrowHeuristically => { self.next_size += size; }
            _ => { }
        }
        (new_words, size)
    }

    #[inline]
    pub fn allocate(&mut self, amount : WordCount32) -> (*mut SegmentBuilder, *mut Word) {
        unsafe {
            match self.segment0.allocate(amount) {
                Some(result) => { return ((&mut self.segment0) as *mut SegmentBuilder, result) }
                None => {}
            }

            //# Need to fall back to additional segments.

            let id = {
                let len = self.more_segments.len();
                if len == 0 { 1 }
                else {
                    let result_ptr = &mut *self.more_segments.as_mut_slice()[len-1] as *mut SegmentBuilder;
                    match self.more_segments.as_mut_slice()[len - 1].allocate(amount) {
                        Some(result) => { return (result_ptr, result) }
                        None => { len + 1 }
                    }
                }};

            let (words, size) = self.allocate_owned_memory(amount);
            let mut new_builder = box SegmentBuilder::new(self, id as u32, words, size);
            let builder_ptr = &mut *new_builder as *mut SegmentBuilder;

            self.more_segments.push(new_builder);

            (builder_ptr, (*builder_ptr).allocate(amount).unwrap() )
        }
    }

    pub fn get_segment(&mut self, id : SegmentId) -> *mut SegmentBuilder {
        if id == 0 {
            &mut self.segment0 as *mut SegmentBuilder
        } else {
            &mut *self.more_segments.as_mut_slice()[(id - 1) as uint] as *mut SegmentBuilder
        }
    }

    pub fn get_segments_for_output<T>(&self, cont : |&[&[Word]]| -> T) -> T {
        unsafe {
            if self.more_segments.len() == 0 {
                let v = ::std::slice::from_raw_buf::<Word>(
                    &self.segment0.reader.ptr,
                    self.segment0.current_size() as uint);
                cont(&[v])
            } else {
                let mut result = Vec::new();
                result.push(::std::mem::transmute(
                    ::std::raw::Slice { data : self.segment0.reader.ptr,
                                      len : self.segment0.current_size() as uint}));

                for seg in self.more_segments.iter() {
                    result.push(::std::mem::transmute(
                        ::std::raw::Slice { data : seg.reader.ptr,
                                            len : seg.current_size() as uint}));
                }
                cont(result.as_slice())
            }
        }
    }

    pub fn get_cap_table<'a>(&'a self) -> &'a [Option<Box<ClientHook+Send>>] {
        self.cap_table.as_slice()
    }

    pub fn inject_cap(&mut self, cap : Box<ClientHook+Send>) -> u32 {
        self.cap_table.push(Some(cap));
        self.cap_table.len() as u32 - 1
    }
}

#[deriving(Copy)]
pub enum ArenaPtr {
    Reader(*const ReaderArena),
    Builder(*mut BuilderArena),
    Null
}

impl ArenaPtr {
    pub fn try_get_segment(&self, id : SegmentId) -> *const SegmentReader {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (&*reader).try_get_segment(id)
                }
                &ArenaPtr::Builder(builder) => {
                    if id == 0 {
                        &(*builder).segment0.reader as *const SegmentReader
                    } else {
                        &(*builder).more_segments.as_slice()[id as uint - 1].reader as *const SegmentReader
                    }
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

    pub fn extract_cap(&self, index : uint) -> Option<Box<ClientHook+Send>> {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    if index < (*reader).cap_table.len() {
                        match (*reader).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Builder(builder) => {
                    if index < (*builder).cap_table.len() {
                        match (*builder).cap_table.as_slice()[index] {
                            Some( ref hook ) => { Some(hook.copy()) }
                            None => {
                                None
                            }
                        }
                    } else {
                        None
                    }
                }
                &ArenaPtr::Null => {
                    panic!();
                }
            }
        }
    }

    pub fn fail_fast(&self) -> bool {
        unsafe {
            match self {
                &ArenaPtr::Reader(reader) => {
                    (*reader).fail_fast
                }
                &ArenaPtr::Builder(builder) => {
                    (*builder).fail_fast
                }
                &ArenaPtr::Null => {
                    panic!()
                }
            }
        }
    }

}
<|endoftext|>


<commit_before>extern mod extra;
extern mod std;

use std::hashmap::HashMap;
use std::path::PosixPath;

use std::io::{File,result, IoError};
use std::io::mem::BufReader;

use std::num::FromPrimitive;

use std::str::from_utf8_owned_opt;

use extra::future::Future;
use extra::arc::Arc;

mod util;
mod class;
mod def;

fn build_classpath(invar : &str) -> ~[~str] {
	// current folder is always included
	let mut v = ~[~"."];

	// TODO: how to construct a vector directly from an iter?
	for s in invar.split_str(";")
		.map(|s : &str| { s.trim().to_owned() })
		.filter(|s : &~str| {s.len() > 0}){

		v.push(s);
	}
	return v;
}

pub struct ClassLoader {
	priv classpath : ~[~str],
	priv classes : HashMap<~str, ~class::JavaClass>
}

static INITIAL_CLASSLOADER_CAPACITY : uint = 1024;

impl ClassLoader {

	// ----------------------------------------------
	// Constructs a ClassLoader given a CLASSPATH that consists of one or
	// more .class search paths separated by semicolons.
	pub fn new(classpath : &str) -> ~ClassLoader {
		~ClassLoader {
			classpath : build_classpath(classpath),
			classes : HashMap::with_capacity(INITIAL_CLASSLOADER_CAPACITY),
		}
	}

	// ----------------------------------------------
	// Loads a class given its fully qualified class name.
	//
	// This triggers recursive loading of dependent classes.
	//
	// add_from_classfile("de.fruits.Apple") loads <class-path>/de/fruits/Apple.class
	pub fn add_from_classfile(self, name : &str) ->
		Future<
			Result<Arc<class::JavaClass>,~str>
		>
	{
		let cname = name.to_owned();
		let pname = cname.replace(&".", "/") + ".class";
		do Future::spawn {
			for path in self.classpath.iter() {

				match result(|| {
					let p = *path + "/" + pname;
					debug!("load class {}, trying path {}", cname, p);
					File::open(&PosixPath::new(p)).read_to_end()
				}) {
					Err(e) => continue,
					Ok(bytes) => {
						debug!("found .class file");
						return self.add_from_classfile_bytes(bytes)
							.unwrap()
					}
				};
			}
			return Err(~"failed to locate class file for " + cname);
		}
	}

	// IMPL

	// ----------------------------------------------
	fn add_from_classfile_bytes(self, bytes : ~[u8]) ->
		Future<
			Result<Arc<class::JavaClass>,~str>
		>
	{
		do Future::spawn {
			match result(|| {
				let mut reader = BufReader::new(bytes);

				let magic = reader.read_be_u32() as uint;
				if magic != 0xCAFEBABE {
					return Err(~"magic word not found");
				}

				let minor = reader.read_be_u16() as uint;
				let major = reader.read_be_u16() as uint;

				// TODO: check whether we support this format
				debug!("class file version {}.{}", major, minor);

				let cpool_count = reader.read_be_u16() as uint;
				if cpool_count == 0 {
					return Err(~"invalid constant pool size");
				}

				debug!("{} constant pool entries", cpool_count - 1);
				let mut constants : ~[def::Constant] = ~[];

				// read constant pool
				let mut i = 1;
				while i < cpool_count {
					let tag = reader.read_u8();
					let parsed_tag : Option<def::ConstantPoolTags> =
						FromPrimitive::from_u8(tag);

					let mut skip = 0;
					let maybe_centry = match parsed_tag {
						None => Err(format!("constant pool tag not recognized: {}", tag)),
						Some(c) => {
							ClassLoader::read_cpool_entry_body(c,
								&mut reader as &mut Reader,
								cpool_count as uint,
								&mut skip
							)
						}
					};

					// if that was ok, add it to the list and advance
					match maybe_centry {
						Err(e) => return Err(e),
						Ok(centry) => {
							debug!("adding constant pool entry: {}", parsed_tag.to_str());
							constants.push(centry)
						}
					}

					i += skip + 1;
				}

				let access = reader.read_be_u16() as uint;
				let this_class = reader.read_be_u16() as uint;
				let super_class = reader.read_be_u16() as uint;
				let ifaces_count = reader.read_be_u16() as uint;

				// read interfaces

				let fields_count = reader.read_be_u16() as uint;
				let methods_count = reader.read_be_u16() as uint;

				// read methods

				let attrs_count = reader.read_be_u16() as uint;

				// read attributes
				return Ok(Arc::new(class::JavaClass::new()))
			}) {
				Err(e) => Err(~"classloader: unexpected end-of-file or read error"),
				Ok(T) => T
			}
		}
	}

	// ----------------------------------------------
	fn read_cpool_entry_body(tag : def::ConstantPoolTags, reader : &mut Reader, count : uint,
		skip : &mut uint) ->
		Result<def::Constant, ~str>
	{
		let mut err : Option<~str> = None;
		let cindex = || {
			// indices are in [1,count)
			let index = reader.read_be_u16();
			if index == 0 || (index as uint) >= count {
				err = Some(format!("constant pool index out of range: {}", index));
			}
			return index;
		};

		// TODO: verify the type of cross-referenced cpool entries
		// TODO: do the read_be .. variants properly raise io_error
		// for our caller to trap?

		let res = match tag {
			def::CONSTANT_class =>
				def::CONSTANT_class_info(cindex()),
			def::CONSTANT_fieldref =>
				def::CONSTANT_fieldref_info(cindex(), cindex()),
			def::CONSTANT_methodref =>
				def::CONSTANT_methodref_info(cindex(), cindex()),
			def::CONSTANT_ifacemethodref =>
				def::CONSTANT_ifacemethodref_info(cindex(), cindex()),
			def::CONSTANT_string =>
				def::CONSTANT_string_info(cindex()),
			def::CONSTANT_integer =>
				def::CONSTANT_integer_info(reader.read_be_i32()),
			def::CONSTANT_float =>
				def::CONSTANT_float_info(reader.read_be_f32()),
			def::CONSTANT_long =>
				def::CONSTANT_long_info(reader.read_be_i64()),
			def::CONSTANT_double =>
				def::CONSTANT_double_info(reader.read_be_f64()),
			def::CONSTANT_nameandtype =>
				def::CONSTANT_nameandtype_info(cindex(),cindex()),
			def::CONSTANT_utf8 => {
				let length = reader.read_be_u16() as uint;
				let raw = reader.read_bytes(length);

				// TODO: Java uses a "modified UTF8", which
				//  - encodes NIL as two bytes
				//  - uss two three-byte sequences to encode four byte encodings
				let s = from_utf8_owned_opt(raw);
				match s {
					None => {
						err = Some(~"constant pool entry is not  valid UTF8 string");
						def::CONSTANT_utf8_info(~"")
					},
					Some(s) => def::CONSTANT_utf8_info(s)
				}
			},
			def::CONSTANT_methodhandle =>
				def::CONSTANT_methodhandle_info(reader.read_u8(), cindex()),
			def::CONSTANT_methodtype =>
				def::CONSTANT_methodtype_info(cindex()),
			def::CONSTANT_invokedynamic =>
				def::CONSTANT_invokedynamic_info(reader.read_be_u16(), cindex()),
		};

		// some cpool entries take two indices. According to the spec,
		// this was "a poor choice".
		*skip = match tag {
			def::CONSTANT_long | def::CONSTANT_double => 1,
			_ => 0
		};

		match err {
			None => Ok(res),
			Some(msg) => Err(msg)
		}
	}
}

#[test]
fn test_class_path_decomposition() {
	let cp = build_classpath("~/some/other/bar; /bar/baz;dir ;");
	assert_eq!(cp,~[~".",~"~/some/other/bar", ~"/bar/baz", ~"dir"]);
}

#[test]
fn test_class_loader_fail() {
	let cl = ClassLoader::new("");
	assert!(cl.add_from_classfile("FooClassDoesNotExist").unwrap().is_err());
}

#[test]
fn test_class_loader_good() {
	let cl = ClassLoader::new("../test/java");
	let v = cl.add_from_classfile("EmptyClass").unwrap();
	util::assert_no_err(v);
}

<commit_msg>classloader: log UTF8 cpool entries<commit_after>extern mod extra;
extern mod std;

use std::hashmap::HashMap;
use std::path::PosixPath;

use std::io::{File,result, IoError};
use std::io::mem::BufReader;

use std::num::FromPrimitive;

use std::str::from_utf8_owned_opt;

use extra::future::Future;
use extra::arc::Arc;

mod util;
mod class;
mod def;

fn build_classpath(invar : &str) -> ~[~str] {
	// current folder is always included
	let mut v = ~[~"."];

	// TODO: how to construct a vector directly from an iter?
	for s in invar.split_str(";")
		.map(|s : &str| { s.trim().to_owned() })
		.filter(|s : &~str| {s.len() > 0}){

		v.push(s);
	}
	return v;
}

pub struct ClassLoader {
	priv classpath : ~[~str],
	priv classes : HashMap<~str, ~class::JavaClass>
}

static INITIAL_CLASSLOADER_CAPACITY : uint = 1024;

impl ClassLoader {

	// ----------------------------------------------
	// Constructs a ClassLoader given a CLASSPATH that consists of one or
	// more .class search paths separated by semicolons.
	pub fn new(classpath : &str) -> ~ClassLoader {
		~ClassLoader {
			classpath : build_classpath(classpath),
			classes : HashMap::with_capacity(INITIAL_CLASSLOADER_CAPACITY),
		}
	}

	// ----------------------------------------------
	// Loads a class given its fully qualified class name.
	//
	// This triggers recursive loading of dependent classes.
	//
	// add_from_classfile("de.fruits.Apple") loads <class-path>/de/fruits/Apple.class
	pub fn add_from_classfile(self, name : &str) ->
		Future<
			Result<Arc<class::JavaClass>,~str>
		>
	{
		let cname = name.to_owned();
		let pname = cname.replace(&".", "/") + ".class";
		do Future::spawn {
			for path in self.classpath.iter() {

				match result(|| {
					let p = *path + "/" + pname;
					debug!("load class {}, trying path {}", cname, p);
					File::open(&PosixPath::new(p)).read_to_end()
				}) {
					Err(e) => continue,
					Ok(bytes) => {
						debug!("found .class file");
						return self.add_from_classfile_bytes(bytes)
							.unwrap()
					}
				};
			}
			return Err(~"failed to locate class file for " + cname);
		}
	}

	// IMPL

	// ----------------------------------------------
	fn add_from_classfile_bytes(self, bytes : ~[u8]) ->
		Future<
			Result<Arc<class::JavaClass>,~str>
		>
	{
		do Future::spawn {
			match result(|| {
				let mut reader = BufReader::new(bytes);

				let magic = reader.read_be_u32() as uint;
				if magic != 0xCAFEBABE {
					return Err(~"magic word not found");
				}

				let minor = reader.read_be_u16() as uint;
				let major = reader.read_be_u16() as uint;

				// TODO: check whether we support this format
				debug!("class file version {}.{}", major, minor);

				let cpool_count = reader.read_be_u16() as uint;
				if cpool_count == 0 {
					return Err(~"invalid constant pool size");
				}

				debug!("{} constant pool entries", cpool_count - 1);
				let mut constants : ~[def::Constant] = ~[];

				// read constant pool
				let mut i = 1;
				while i < cpool_count {
					let tag = reader.read_u8();
					let parsed_tag : Option<def::ConstantPoolTags> =
						FromPrimitive::from_u8(tag);

					let mut skip = 0;
					let maybe_centry = match parsed_tag {
						None => Err(format!("constant pool tag not recognized: {}", tag)),
						Some(c) => {
							ClassLoader::read_cpool_entry_body(c,
								&mut reader as &mut Reader,
								cpool_count as uint,
								&mut skip
							)
						}
					};

					// if that was ok, add it to the list and advance
					match maybe_centry {
						Err(e) => return Err(e),
						Ok(centry) => {
							debug!("adding constant pool entry: {}", parsed_tag.to_str());
							constants.push(centry)
						}
					}

					i += skip + 1;
				}

				let access = reader.read_be_u16() as uint;
				let this_class = reader.read_be_u16() as uint;
				let super_class = reader.read_be_u16() as uint;
				let ifaces_count = reader.read_be_u16() as uint;

				// read interfaces

				let fields_count = reader.read_be_u16() as uint;
				let methods_count = reader.read_be_u16() as uint;

				// read methods

				let attrs_count = reader.read_be_u16() as uint;

				// read attributes
				return Ok(Arc::new(class::JavaClass::new()))
			}) {
				Err(e) => Err(~"classloader: unexpected end-of-file or read error"),
				Ok(T) => T
			}
		}
	}

	// ----------------------------------------------
	fn read_cpool_entry_body(tag : def::ConstantPoolTags, reader : &mut Reader, count : uint,
		skip : &mut uint) ->
		Result<def::Constant, ~str>
	{
		let mut err : Option<~str> = None;
		let cindex = || {
			// indices are in [1,count)
			let index = reader.read_be_u16();
			if index == 0 || (index as uint) >= count {
				err = Some(format!("constant pool index out of range: {}", index));
			}
			return index;
		};

		// TODO: verify the type of cross-referenced cpool entries
		// TODO: do the read_be .. variants properly raise io_error
		// for our caller to trap?

		let res = match tag {
			def::CONSTANT_class =>
				def::CONSTANT_class_info(cindex()),
			def::CONSTANT_fieldref =>
				def::CONSTANT_fieldref_info(cindex(), cindex()),
			def::CONSTANT_methodref =>
				def::CONSTANT_methodref_info(cindex(), cindex()),
			def::CONSTANT_ifacemethodref =>
				def::CONSTANT_ifacemethodref_info(cindex(), cindex()),
			def::CONSTANT_string =>
				def::CONSTANT_string_info(cindex()),
			def::CONSTANT_integer =>
				def::CONSTANT_integer_info(reader.read_be_i32()),
			def::CONSTANT_float =>
				def::CONSTANT_float_info(reader.read_be_f32()),
			def::CONSTANT_long =>
				def::CONSTANT_long_info(reader.read_be_i64()),
			def::CONSTANT_double =>
				def::CONSTANT_double_info(reader.read_be_f64()),
			def::CONSTANT_nameandtype =>
				def::CONSTANT_nameandtype_info(cindex(),cindex()),
			def::CONSTANT_utf8 => {
				let length = reader.read_be_u16() as uint;
				let raw = reader.read_bytes(length);

				// TODO: Java uses a "modified UTF8", which
				//  - encodes NIL as two bytes
				//  - uss two three-byte sequences to encode four byte encodings
				let s = from_utf8_owned_opt(raw);
				match s {
					None => {
						err = Some(~"constant pool entry is not  valid UTF8 string");
						def::CONSTANT_utf8_info(~"")
					},
					Some(s) => {
						debug!("utf8 string: {}", s);
						def::CONSTANT_utf8_info(s)
					}
				}
			},
			def::CONSTANT_methodhandle =>
				def::CONSTANT_methodhandle_info(reader.read_u8(), cindex()),
			def::CONSTANT_methodtype =>
				def::CONSTANT_methodtype_info(cindex()),
			def::CONSTANT_invokedynamic =>
				def::CONSTANT_invokedynamic_info(reader.read_be_u16(), cindex()),
		};

		// some cpool entries take two indices. According to the spec,
		// this was "a poor choice".
		*skip = match tag {
			def::CONSTANT_long | def::CONSTANT_double => 1,
			_ => 0
		};

		match err {
			None => Ok(res),
			Some(msg) => Err(msg)
		}
	}
}

#[test]
fn test_class_path_decomposition() {
	let cp = build_classpath("~/some/other/bar; /bar/baz;dir ;");
	assert_eq!(cp,~[~".",~"~/some/other/bar", ~"/bar/baz", ~"dir"]);
}

#[test]
fn test_class_loader_fail() {
	let cl = ClassLoader::new("");
	assert!(cl.add_from_classfile("FooClassDoesNotExist").unwrap().is_err());
}

#[test]
fn test_class_loader_good() {
	let cl = ClassLoader::new("../test/java");
	let v = cl.add_from_classfile("EmptyClass").unwrap();
	util::assert_no_err(v);
}

<|endoftext|>


<commit_before>use ast;
use lexer;

struct ParserState {
    tokens: @[lexer::Token],
    last_token: lexer::Token,
    pos: uint
}

impl ParserState {
    fn peek(&mut self) -> lexer::Token {
        self.last_token = self.tokens[self.pos];
        self.last_token
    }

    fn expect(&mut self, type_: lexer::TokenType) -> lexer::Token {
        let token = self.pop();
        if token.type_ != type_ {
            self.fail(fmt!("expected %?, got \"%s\" (%?)"
                           type_, token.value, token.type_));
        }
        token
    }

    fn expect_with_value(&mut self, type_: lexer::TokenType, value: @str) -> lexer::Token {
        let token = self.expect(type_);
        if token.value != value {
            self.fail(fmt!("expected \"%s\", got \"%s\" (%?)"
                           value, token.value, token.type_));
        }
        token
    }

    fn pop(&mut self) -> lexer::Token {
        let token = self.peek();
        self.pos += 1;
        token
    }

    fn fail(&mut self, msg: ~str) -> ! {
        fail!(fmt!("parser failure: %s (line %?, col %?)",
                   msg, self.last_token.lineno, self.last_token.colno));
    }
}

fn parse_symbol(state: @mut ParserState) -> ast::Exp {
    let token = state.expect(lexer::SYMBOL);
    ast::Exp {
        exp: ast::SymbolExpression(ast::Sym(token.value)),
        lineno: token.lineno
    }
}

fn parse_module_name(state: @mut ParserState) -> ast::ModuleName {
    let mut parts = @[];

    match parse_symbol(state) {
        ast::Exp { exp: ast::SymbolExpression(part), lineno: _ } => parts += [part],
        _ => state.fail(~"irrefutable pattern match refuted?!")
    }

    loop {
        let token = state.peek();

        if (token.type_ != lexer::DOT) {
            break;
        }
        state.pop();

        match parse_symbol(state) {
            ast::Exp { exp: ast::SymbolExpression(part), lineno: _ } => parts += [part],
            _ => state.fail(~"irrefutable pattern match refuted?!")
        }
    }

    ast::ModuleName(parts)
}

fn parse_import_declaration(state: @mut ParserState) -> ast::ImportDeclaration {
    let first = state.expect_with_value(lexer::KEYWORD, @"import");
    let token = state.peek();

    let mut qualified = token.type_ == lexer::KEYWORD &&
                        token.value == @"qualified";

    if qualified {
        state.pop();
    }

    ast::ImportDeclaration {
        module: parse_module_name(state),
        qualified: qualified,
        lineno: first.lineno
    }
}

fn parse_record_name(state: @mut ParserState) -> ast::Exp {
    let token = state.expect(lexer::RECORD_NAME);
    ast::Exp {
        exp: ast::RecordNameExpression(ast::RecordName(token.value)),
        lineno: token.lineno
    }
}

fn parse_record_body(state: @mut ParserState) -> @[ast::Sym] {
    let mut slots = @[];

    match parse_symbol(state) {
        ast::Exp { exp: ast::SymbolExpression(slot), lineno: _ } => slots += [slot],
        _ => state.fail(~"irrefutable pattern match refuted?!")
    }

    loop {
        if state.peek().type_ != lexer::COMMA {
            break;
        }
        state.pop();

        if state.peek().type_ == lexer::SYMBOL {
            match parse_symbol(state) {
                ast::Exp { exp: ast::SymbolExpression(slot), lineno: _ } => slots += [slot],
                _ => state.fail(~"irrefutable pattern match refuted?!")
            }
        } else {
            break;
        }
    }

    slots
}

fn parse_record_declaration(state: @mut ParserState) -> ast::RecordDeclaration {
    let first = state.expect_with_value(lexer::KEYWORD, @"record");
    let record_name: ast::RecordName;

    match parse_record_name(state) {
        ast::Exp { exp: ast::RecordNameExpression(name), lineno: _ } => record_name = name,
        _ => state.fail(~"irrefutable pattern match refuted?!")
    }

    let mut slots = @[];

    if state.peek().type_ == lexer::LPAREN {
        state.pop();
        if state.peek().type_ != lexer::RPAREN {
            slots = parse_record_body(state);
        }
        state.expect(lexer::RPAREN);
    }

    ast::RecordDeclaration {
        name: record_name,
        slots: slots,
        lineno: first.lineno
    }
}

fn parse_lambda(state: @mut ParserState) -> ast::Exp {
    let first = state.expect_with_value(lexer::KEYWORD, @"fn");
    state.expect(lexer::LPAREN);
    // TODO: the rest of this
    state.expect(lexer::RPAREN);
    state.expect(lexer::ARROW);
    ast::Exp {
        exp: ast::LambdaExpression(@[ast::AnyPattern], @parse_expression(state)),
        lineno: first.lineno
    }
}

fn parse_list_body(state: @mut ParserState) -> @[ast::Exp] {
    let mut items = @[parse_expression(state)];

    loop {
        if state.peek().type_ != lexer::COMMA {
            break;
        }
        state.pop();

        if state.peek().type_ == lexer::RBRACK {
            break;
        }

        items += [parse_expression(state)];
    }

    items
}

fn parse_list(state: @mut ParserState) -> ast::Exp {
    let first = state.expect(lexer::LBRACK);

    let mut items = @[];

    if state.peek().type_ != lexer::RBRACK {
        items = parse_list_body(state);
    }

    state.expect(lexer::RBRACK);

    ast::Exp {
        exp: ast::ListExpression(items),
        lineno: first.lineno
    }
}

fn parse_primary(state: @mut ParserState) -> ast::Exp {
    let first = state.peek();

    match first.type_ {
        lexer::KEYWORD => parse_lambda(state),
        lexer::LPAREN => parse_parenthesized(state),
        lexer::LBRACK => parse_list(state),
        lexer::SYMBOL => parse_symbol(state),
        _ => state.fail(~"expected one of KEYWORD, LPAREN, LBRACK, SYMBOL")
    }
}

fn parse_parenthesized(state: @mut ParserState) -> ast::Exp {
    state.expect(lexer::LPAREN);
    let exp = parse_expression(state);
    state.expect(lexer::RPAREN);
    exp
}

fn parse_expression(state: @mut ParserState) -> ast::Exp {
    // TODO: the rest of this
    parse_primary(state)
}

fn parse_statements(state: @mut ParserState) -> (@[ast::Exp], bool) {
    let mut statements = @[];
    let mut trailing_semi = false;

    statements += [parse_expression(state)];

    loop {
        if state.peek().type_ != lexer::SEMICOLON {
            break;
        }
        state.pop();

        if state.peek().type_ == lexer::RBRACE ||
           state.peek().type_ == lexer::EOF {
            trailing_semi = true;
            break;
        }

        statements += [parse_expression(state)];
    }

    (statements, trailing_semi)
}

fn parse_program(state: @mut ParserState) -> ast::Program {
    let mut imports = @[];
    let mut records = @[];
    let mut body = @[];

    loop {
        let token = state.peek();
        if (token.type_ == lexer::KEYWORD && token.value == @"import") {
            imports += [parse_import_declaration(state)];
            state.expect(lexer::SEMICOLON);
        } else {
            break;
        }
    }

    loop {
        let token = state.peek();
        if (token.type_ == lexer::KEYWORD && token.value == @"record") {
            records += [parse_record_declaration(state)];
            state.expect(lexer::SEMICOLON);
        } else {
            break;
        }
    }

    if state.peek().type_ != lexer::EOF {
        match parse_statements(state) {
            (b, _) => body = b
        }
    }

    state.expect(lexer::EOF);

    ast::Program {
        imports: imports,
        records: records,
        body: body
    }
}

pub fn parse(tokens: @[lexer::Token]) -> ast::Program {
    parse_program(@mut ParserState {
        tokens: tokens,
        last_token: tokens[0],
        pos: 0
    })
}
<commit_msg>let mut qualified -> let qualified<commit_after>use ast;
use lexer;

struct ParserState {
    tokens: @[lexer::Token],
    last_token: lexer::Token,
    pos: uint
}

impl ParserState {
    fn peek(&mut self) -> lexer::Token {
        self.last_token = self.tokens[self.pos];
        self.last_token
    }

    fn expect(&mut self, type_: lexer::TokenType) -> lexer::Token {
        let token = self.pop();
        if token.type_ != type_ {
            self.fail(fmt!("expected %?, got \"%s\" (%?)"
                           type_, token.value, token.type_));
        }
        token
    }

    fn expect_with_value(&mut self, type_: lexer::TokenType, value: @str) -> lexer::Token {
        let token = self.expect(type_);
        if token.value != value {
            self.fail(fmt!("expected \"%s\", got \"%s\" (%?)"
                           value, token.value, token.type_));
        }
        token
    }

    fn pop(&mut self) -> lexer::Token {
        let token = self.peek();
        self.pos += 1;
        token
    }

    fn fail(&mut self, msg: ~str) -> ! {
        fail!(fmt!("parser failure: %s (line %?, col %?)",
                   msg, self.last_token.lineno, self.last_token.colno));
    }
}

fn parse_symbol(state: @mut ParserState) -> ast::Exp {
    let token = state.expect(lexer::SYMBOL);
    ast::Exp {
        exp: ast::SymbolExpression(ast::Sym(token.value)),
        lineno: token.lineno
    }
}

fn parse_module_name(state: @mut ParserState) -> ast::ModuleName {
    let mut parts = @[];

    match parse_symbol(state) {
        ast::Exp { exp: ast::SymbolExpression(part), lineno: _ } => parts += [part],
        _ => state.fail(~"irrefutable pattern match refuted?!")
    }

    loop {
        let token = state.peek();

        if (token.type_ != lexer::DOT) {
            break;
        }
        state.pop();

        match parse_symbol(state) {
            ast::Exp { exp: ast::SymbolExpression(part), lineno: _ } => parts += [part],
            _ => state.fail(~"irrefutable pattern match refuted?!")
        }
    }

    ast::ModuleName(parts)
}

fn parse_import_declaration(state: @mut ParserState) -> ast::ImportDeclaration {
    let first = state.expect_with_value(lexer::KEYWORD, @"import");
    let token = state.peek();

    let qualified = token.type_ == lexer::KEYWORD &&
                    token.value == @"qualified";

    if qualified {
        state.pop();
    }

    ast::ImportDeclaration {
        module: parse_module_name(state),
        qualified: qualified,
        lineno: first.lineno
    }
}

fn parse_record_name(state: @mut ParserState) -> ast::Exp {
    let token = state.expect(lexer::RECORD_NAME);
    ast::Exp {
        exp: ast::RecordNameExpression(ast::RecordName(token.value)),
        lineno: token.lineno
    }
}

fn parse_record_body(state: @mut ParserState) -> @[ast::Sym] {
    let mut slots = @[];

    match parse_symbol(state) {
        ast::Exp { exp: ast::SymbolExpression(slot), lineno: _ } => slots += [slot],
        _ => state.fail(~"irrefutable pattern match refuted?!")
    }

    loop {
        if state.peek().type_ != lexer::COMMA {
            break;
        }
        state.pop();

        if state.peek().type_ == lexer::SYMBOL {
            match parse_symbol(state) {
                ast::Exp { exp: ast::SymbolExpression(slot), lineno: _ } => slots += [slot],
                _ => state.fail(~"irrefutable pattern match refuted?!")
            }
        } else {
            break;
        }
    }

    slots
}

fn parse_record_declaration(state: @mut ParserState) -> ast::RecordDeclaration {
    let first = state.expect_with_value(lexer::KEYWORD, @"record");
    let record_name: ast::RecordName;

    match parse_record_name(state) {
        ast::Exp { exp: ast::RecordNameExpression(name), lineno: _ } => record_name = name,
        _ => state.fail(~"irrefutable pattern match refuted?!")
    }

    let mut slots = @[];

    if state.peek().type_ == lexer::LPAREN {
        state.pop();
        if state.peek().type_ != lexer::RPAREN {
            slots = parse_record_body(state);
        }
        state.expect(lexer::RPAREN);
    }

    ast::RecordDeclaration {
        name: record_name,
        slots: slots,
        lineno: first.lineno
    }
}

fn parse_lambda(state: @mut ParserState) -> ast::Exp {
    let first = state.expect_with_value(lexer::KEYWORD, @"fn");
    state.expect(lexer::LPAREN);
    // TODO: the rest of this
    state.expect(lexer::RPAREN);
    state.expect(lexer::ARROW);
    ast::Exp {
        exp: ast::LambdaExpression(@[ast::AnyPattern], @parse_expression(state)),
        lineno: first.lineno
    }
}

fn parse_list_body(state: @mut ParserState) -> @[ast::Exp] {
    let mut items = @[parse_expression(state)];

    loop {
        if state.peek().type_ != lexer::COMMA {
            break;
        }
        state.pop();

        if state.peek().type_ == lexer::RBRACK {
            break;
        }

        items += [parse_expression(state)];
    }

    items
}

fn parse_list(state: @mut ParserState) -> ast::Exp {
    let first = state.expect(lexer::LBRACK);

    let mut items = @[];

    if state.peek().type_ != lexer::RBRACK {
        items = parse_list_body(state);
    }

    state.expect(lexer::RBRACK);

    ast::Exp {
        exp: ast::ListExpression(items),
        lineno: first.lineno
    }
}

fn parse_primary(state: @mut ParserState) -> ast::Exp {
    let first = state.peek();

    match first.type_ {
        lexer::KEYWORD => parse_lambda(state),
        lexer::LPAREN => parse_parenthesized(state),
        lexer::LBRACK => parse_list(state),
        lexer::SYMBOL => parse_symbol(state),
        _ => state.fail(~"expected one of KEYWORD, LPAREN, LBRACK, SYMBOL")
    }
}

fn parse_parenthesized(state: @mut ParserState) -> ast::Exp {
    state.expect(lexer::LPAREN);
    let exp = parse_expression(state);
    state.expect(lexer::RPAREN);
    exp
}

fn parse_expression(state: @mut ParserState) -> ast::Exp {
    // TODO: the rest of this
    parse_primary(state)
}

fn parse_statements(state: @mut ParserState) -> (@[ast::Exp], bool) {
    let mut statements = @[];
    let mut trailing_semi = false;

    statements += [parse_expression(state)];

    loop {
        if state.peek().type_ != lexer::SEMICOLON {
            break;
        }
        state.pop();

        if state.peek().type_ == lexer::RBRACE ||
           state.peek().type_ == lexer::EOF {
            trailing_semi = true;
            break;
        }

        statements += [parse_expression(state)];
    }

    (statements, trailing_semi)
}

fn parse_program(state: @mut ParserState) -> ast::Program {
    let mut imports = @[];
    let mut records = @[];
    let mut body = @[];

    loop {
        let token = state.peek();
        if (token.type_ == lexer::KEYWORD && token.value == @"import") {
            imports += [parse_import_declaration(state)];
            state.expect(lexer::SEMICOLON);
        } else {
            break;
        }
    }

    loop {
        let token = state.peek();
        if (token.type_ == lexer::KEYWORD && token.value == @"record") {
            records += [parse_record_declaration(state)];
            state.expect(lexer::SEMICOLON);
        } else {
            break;
        }
    }

    if state.peek().type_ != lexer::EOF {
        match parse_statements(state) {
            (b, _) => body = b
        }
    }

    state.expect(lexer::EOF);

    ast::Program {
        imports: imports,
        records: records,
        body: body
    }
}

pub fn parse(tokens: @[lexer::Token]) -> ast::Program {
    parse_program(@mut ParserState {
        tokens: tokens,
        last_token: tokens[0],
        pos: 0
    })
}
<|endoftext|>