<commit_before>extern crate fuse;
extern crate libc;

use self::fuse::{FileAttr, FileType};
use std::convert::From;
use std::ffi::OsStr;
use std::io::{Result, Error};
use std::path::{PathBuf, Path};
use std::rc::Rc;
use std::vec::Vec;
use std::cell::RefCell;
use std::collections::HashSet;

use fs;
mod wrapper;
mod buffer;
mod page;
mod link;
mod reader;

fn to_fuse_file_type(file_type: libc::mode_t) -> FileType {
    match file_type & libc::S_IFMT {
        libc::S_IFLNK => FileType::Symlink,
        libc::S_IFREG => FileType::RegularFile,
        libc::S_IFBLK => FileType::BlockDevice,
        libc::S_IFDIR => FileType::Directory,
        libc::S_IFCHR => FileType::CharDevice,
        libc::S_IFIFO => FileType::NamedPipe,
        _ => FileType::RegularFile,
    }
}

fn to_fuse_file_attr(size: libc::int64_t, file_type: libc::mode_t, attr: FileAttr) -> FileAttr {
    FileAttr {
        ino: 0, // dummy
        size: size as u64,
        blocks: (size as u64 + 4095) / 4096,
        atime: attr.atime,
        mtime: attr.mtime,
        ctime: attr.ctime,
        crtime: attr.crtime, // mac only
        kind: to_fuse_file_type(file_type),
        perm: attr.perm,
        nlink: 0,
        uid: attr.uid,
        gid: attr.gid,
        rdev: attr.rdev,
        flags: 0, // mac only
    }
}

struct ArchivedFile {
    archive: Rc<Box<fs::File>>,
    attr: FileAttr,
    path: PathBuf,
}

impl ArchivedFile {
    fn new(archive: Rc<Box<fs::File>>, attr: FileAttr, path: PathBuf) -> ArchivedFile {
        ArchivedFile {
            archive: archive,
            attr: attr,
            path: path,
        }
    }
}

impl fs::File for ArchivedFile {
    fn getattr(&self) -> Result<FileAttr> {
        Ok(self.attr)
    }

    fn open(&self) -> Result<Box<fs::SeekableRead>> {
        let archive = wrapper::Archive::new(self.archive.open()?);
        let reader = archive.find_open(|e| e.pathname() == self.path)
            .unwrap_or(Err(Error::from_raw_os_error(libc::ENOENT)))?;
        Ok(Box::new(reader))
    }

    fn name(&self) -> &OsStr {
        self.path.file_name().unwrap()
    }
}

struct CacheFile {
    cache: RefCell<reader::Cache>,
    file: Rc<ArchivedFile>,
}

impl CacheFile {
    fn new(file: ArchivedFile, page_manager: Rc<RefCell<page::PageManager>>) -> CacheFile {
        let file = Rc::new(file);
        CacheFile {
            cache: RefCell::new(reader::Cache::new(page_manager, file.clone())),
            file: file,
        }
    }
}

impl fs::File for CacheFile {
    fn getattr(&self) -> Result<FileAttr> {
        self.file.getattr()
    }

    fn open(&self) -> Result<Box<fs::SeekableRead>> {
        self.cache.borrow_mut().make_reader()
    }

    fn name(&self) -> &OsStr {
        self.file.name()
    }
}

struct DirEntry {
    attr: FileAttr,
    path: PathBuf,
}

pub struct Dir {
    archive: Rc<Box<fs::File>>,
    path: PathBuf,
    attr: RefCell<Option<FileAttr>>,
    dents: RefCell<Option<Rc<Vec<DirEntry>>>>,
    page_manager: Rc<RefCell<page::PageManager>>,
}

impl Dir {
    pub fn new(f: Box<fs::File>, page_manager: Rc<RefCell<page::PageManager>>) -> Self {
        Dir {
            archive: Rc::new(f),
            path: PathBuf::new(),
            attr: RefCell::new(None),
            dents: RefCell::new(None),
            page_manager: page_manager,
        }
    }

    fn from_dent(f: Rc<Box<fs::File>>,
                 dent: &DirEntry,
                 dents: Rc<Vec<DirEntry>>,
                 page_manager: Rc<RefCell<page::PageManager>>)
                 -> Self {
        Dir {
            archive: f,
            path: dent.path.clone(),
            attr: RefCell::new(Some(dent.attr)),
            dents: RefCell::new(Some(dents)),
            page_manager: page_manager,
        }
    }

    fn update_cache(&self) -> Result<()> {
        use fs::Dir;
        if self.dents.borrow().is_some() {
            return Ok(());
        }
        let self_attr = self.getattr()?;
        let mut archive = wrapper::Archive::new(self.archive.open()?);
        let mut dents = Vec::new();
        let mut dirs = HashSet::new();
        loop {
            match archive.next_entry() {
                Some(Ok(ent)) => {
                    let path = ent.pathname();
                    let attr = to_fuse_file_attr(ent.size(), ent.filetype(), self_attr);
                    {
                        let mut parent = path.parent();
                        while parent.is_some() {
                            let path = parent.unwrap();
                            if !dirs.insert(PathBuf::from(path)) {
                                dents.push(DirEntry {
                                    attr: self_attr,
                                    path: PathBuf::from(path),
                                });
                            }
                            parent = path.parent();
                        }
                    }
                    if attr.kind != FileType::Directory || !dirs.insert(path.clone()) {
                        dents.push(DirEntry {
                            attr: attr,
                            path: path,
                        });
                    }
                }
                Some(Err(e)) => return Err(e),
                None => break,
            }
        }
        *self.dents.borrow_mut() = Some(Rc::new(dents));
        Ok(())
    }
}

impl fs::Dir for Dir {
    fn open(&self) -> Result<Box<Iterator<Item = Result<fs::Entry>>>> {
        self.update_cache()?;
        Ok(Box::new(DirHandler::open(self)))
    }

    fn lookup(&self, name: &OsStr) -> Result<fs::Entry> {
        self.update_cache()?;
        let lookup_path = self.path.join(name);
        for e in self.dents.borrow().as_ref().unwrap().iter() {
            if e.path == lookup_path {
                if e.attr.kind != FileType::Directory {
                    return Ok(fs::Entry::File(Box::new(CacheFile::new(
                        ArchivedFile::new(
                            self.archive.clone(),
                            e.attr,
                            lookup_path.clone()),
                        self.page_manager.clone()))));
                } else {
                    return Ok(fs::Entry::Dir(Box::new(Dir {
                        archive: self.archive.clone(),
                        path: lookup_path.clone(),
                        attr: RefCell::new(Some(e.attr)),
                        dents: self.dents.clone(),
                        page_manager: self.page_manager
                            .clone(),
                    })));
                }
            }
        }
        Err(Error::from_raw_os_error(libc::ENOENT))
    }

    fn getattr(&self) -> Result<FileAttr> {
        if self.attr.borrow().is_none() {
            *self.attr.borrow_mut() = Some(self.archive
                .getattr()
                .map(|mut attr| {
                    attr.kind = FileType::Directory;
                    attr
                })?);
        }
        Ok(self.attr.borrow().unwrap())
    }

    fn name(&self) -> &OsStr {
        if self.path.as_os_str().is_empty() {
            self.archive.name()
        } else {
            self.path.file_name().unwrap()
        }
    }
}

struct DirHandler {
    archive: Rc<Box<fs::File>>,
    path: PathBuf,
    dents: Rc<Vec<DirEntry>>,
    i: usize,
    page_manager: Rc<RefCell<page::PageManager>>,
}

impl DirHandler {
    fn open(dir: &Dir) -> Self {
        DirHandler {
            archive: dir.archive.clone(),
            path: dir.path.clone(),
            dents: dir.dents.borrow().as_ref().unwrap().clone(),
            i: 0,
            page_manager: dir.page_manager.clone(),
        }
    }
}

impl Iterator for DirHandler {
    type Item = Result<fs::Entry>;

    fn next(&mut self) -> Option<Result<fs::Entry>> {
        let dents = self.dents.as_ref();
        while self.i < dents.len() {
            let e = &dents[self.i];
            self.i += 1;
            if let Ok(sub) = e.path.strip_prefix(self.path.as_path()) {
                let mut iter = sub.iter();
                let name = match iter.next() {
                    Some(name) => name,
                    None => continue,
                };
                if iter.next().is_some() || e.attr.kind == FileType::Directory {
                    return Some(Ok(fs::Entry::Dir(Box::new(Dir::from_dent(self.archive
                                                                              .clone(),
                                                                          e,
                                                                          self.dents
                                                                              .clone(),
                                                                          self.page_manager
                                                                              .clone())))));
                } else {
                    return Some(Ok(fs::Entry::File(Box::new(CacheFile::new(
                        ArchivedFile::new(
                            self.archive.clone(),
                            e.attr,
                            self.path.join(name)),
                        self.page_manager.clone())))));
                }
            }
        }
        None
    }
}

pub struct ArchiveViewer {
    page_manager: Rc<RefCell<page::PageManager>>,
}

impl ArchiveViewer {
    pub fn new(max_bytes: usize) -> Result<ArchiveViewer> {
        wrapper::initialize();
        Ok(ArchiveViewer {
            page_manager: Rc::new(RefCell::new(page::PageManager::new(max_bytes)?)),
        })
    }
}

impl fs::Viewer for ArchiveViewer {
    fn view(&self, e: fs::Entry) -> fs::Entry {
        let is_archive = match e {
            fs::Entry::File(ref f) => {
                match Path::new(f.name())
                    .extension()
                    .and_then(|ext| ext.to_str()) {
                    Some(ext) => {
                        match ext.to_lowercase().as_str() {
                            "zip" => true,
                            "rar" => true,
                            _ => false,
                        }
                    }
                    _ => false,
                }
            }
            _ => false,
        };
        if is_archive {
            if let fs::Entry::File(f) = e {
                return fs::Entry::Dir(Box::new(Dir::new(f, self.page_manager.clone())));
            }
        }
        e
    }
}

#[test]
fn test_iterate_dir() {
    use physical;
    use fs::Dir as FSDir;

    let page_manager = Rc::new(RefCell::new(page::PageManager::new(100 * 1024 * 1024).unwrap()));
    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let zip = root.join("assets/test.zip");
    let zip_dir = Dir::new(Box::new(physical::File::new(zip)), page_manager.clone());
    let entries: Vec<_> = zip_dir.open().unwrap().map(|re| re.unwrap()).collect();
    assert!(entries.iter().all(|e| e.file_type(0).unwrap() == FileType::RegularFile));
    let mut names: Vec<_> = entries.iter().map(|e| PathBuf::from(e.name())).collect();
    names.sort();
    let expect = vec![PathBuf::from("large"), PathBuf::from("small")];
    assert_eq!(names, expect);
}

#[test]
fn test_file_read() {
    use std::fs as stdfs;
    use fs::File;
    use physical;
    use std::io::Read;

    let assets = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("assets");
    let zip = assets.join("test.zip");
    let zip_file = physical::File::new(zip);
    let read_archive = |name| {
        let archive = wrapper::Archive::new(zip_file.open().unwrap());
        let mut r = archive.find_open(|e| e.pathname() == PathBuf::from(name)).unwrap().unwrap();
        let mut v = Vec::<u8>::new();
        r.read_to_end(&mut v).unwrap();
        v
    };
    let read_file = |name| {
        let mut v = Vec::<u8>::new();
        let mut r = stdfs::File::open(assets.join(name)).unwrap();
        r.read_to_end(&mut v).unwrap();
        v
    };

    let small_actual = read_archive("small");
    let small_expect = read_file("small");
    assert_eq!(small_actual, small_expect);

    let large_actual = read_archive("large");
    let large_expect = read_file("large");
    assert_eq!(large_actual, large_expect);
}
<commit_msg>fix cache insertion and iteration<commit_after>extern crate fuse;
extern crate libc;

use self::fuse::{FileAttr, FileType};
use std::convert::From;
use std::ffi::OsStr;
use std::io::{Result, Error};
use std::path::{PathBuf, Path};
use std::rc::Rc;
use std::vec::Vec;
use std::cell::RefCell;
use std::collections::HashSet;

use fs;
mod wrapper;
mod buffer;
mod page;
mod link;
mod reader;

fn to_fuse_file_type(file_type: libc::mode_t) -> FileType {
    match file_type & libc::S_IFMT {
        libc::S_IFLNK => FileType::Symlink,
        libc::S_IFREG => FileType::RegularFile,
        libc::S_IFBLK => FileType::BlockDevice,
        libc::S_IFDIR => FileType::Directory,
        libc::S_IFCHR => FileType::CharDevice,
        libc::S_IFIFO => FileType::NamedPipe,
        _ => FileType::RegularFile,
    }
}

fn to_fuse_file_attr(size: libc::int64_t, file_type: libc::mode_t, attr: FileAttr) -> FileAttr {
    FileAttr {
        ino: 0, // dummy
        size: size as u64,
        blocks: (size as u64 + 4095) / 4096,
        atime: attr.atime,
        mtime: attr.mtime,
        ctime: attr.ctime,
        crtime: attr.crtime, // mac only
        kind: to_fuse_file_type(file_type),
        perm: attr.perm,
        nlink: 0,
        uid: attr.uid,
        gid: attr.gid,
        rdev: attr.rdev,
        flags: 0, // mac only
    }
}

struct ArchivedFile {
    archive: Rc<Box<fs::File>>,
    attr: FileAttr,
    path: PathBuf,
}

impl ArchivedFile {
    fn new(archive: Rc<Box<fs::File>>, attr: FileAttr, path: PathBuf) -> ArchivedFile {
        ArchivedFile {
            archive: archive,
            attr: attr,
            path: path,
        }
    }
}

impl fs::File for ArchivedFile {
    fn getattr(&self) -> Result<FileAttr> {
        Ok(self.attr)
    }

    fn open(&self) -> Result<Box<fs::SeekableRead>> {
        let archive = wrapper::Archive::new(self.archive.open()?);
        let reader = archive
            .find_open(|e| e.pathname() == self.path)
            .unwrap_or(Err(Error::from_raw_os_error(libc::ENOENT)))?;
        Ok(Box::new(reader))
    }

    fn name(&self) -> &OsStr {
        self.path.file_name().unwrap()
    }
}

struct CacheFile {
    cache: RefCell<reader::Cache>,
    file: Rc<ArchivedFile>,
}

impl CacheFile {
    fn new(file: ArchivedFile, page_manager: Rc<RefCell<page::PageManager>>) -> CacheFile {
        let file = Rc::new(file);
        CacheFile {
            cache: RefCell::new(reader::Cache::new(page_manager, file.clone())),
            file: file,
        }
    }
}

impl fs::File for CacheFile {
    fn getattr(&self) -> Result<FileAttr> {
        self.file.getattr()
    }

    fn open(&self) -> Result<Box<fs::SeekableRead>> {
        self.cache.borrow_mut().make_reader()
    }

    fn name(&self) -> &OsStr {
        self.file.name()
    }
}

struct DirEntry {
    attr: FileAttr,
    path: PathBuf,
}

pub struct Dir {
    archive: Rc<Box<fs::File>>,
    path: PathBuf,
    attr: RefCell<Option<FileAttr>>,
    dents: RefCell<Option<Rc<Vec<DirEntry>>>>,
    page_manager: Rc<RefCell<page::PageManager>>,
}

impl Dir {
    pub fn new(f: Box<fs::File>, page_manager: Rc<RefCell<page::PageManager>>) -> Self {
        Dir {
            archive: Rc::new(f),
            path: PathBuf::new(),
            attr: RefCell::new(None),
            dents: RefCell::new(None),
            page_manager: page_manager,
        }
    }

    fn from_parts(f: Rc<Box<fs::File>>,
                  path: PathBuf,
                  attr: FileAttr,
                  dents: Rc<Vec<DirEntry>>,
                  page_manager: Rc<RefCell<page::PageManager>>)
                  -> Self {
        Dir {
            archive: f,
            path: path,
            attr: RefCell::new(Some(attr)),
            dents: RefCell::new(Some(dents)),
            page_manager: page_manager,
        }
    }

    fn update_cache(&self) -> Result<()> {
        use fs::Dir;
        if self.dents.borrow().is_some() {
            return Ok(());
        }
        let self_attr = self.getattr()?;
        let mut archive = wrapper::Archive::new(self.archive.open()?);
        let mut dents = Vec::new();
        let mut dirs = HashSet::new();
        loop {
            match archive.next_entry() {
                Some(Ok(ent)) => {
                    let path = ent.pathname();
                    let attr = to_fuse_file_attr(ent.size(), ent.filetype(), self_attr);
                    {
                        let mut parent = path.parent();
                        while parent.is_some() {
                            let path = parent.unwrap();
                            if dirs.insert(PathBuf::from(path)) {
                                dents.push(DirEntry {
                                               attr: self_attr,
                                               path: PathBuf::from(path),
                                           });
                            }
                            parent = path.parent();
                        }
                    }
                    if attr.kind != FileType::Directory || dirs.insert(path.clone()) {
                        dents.push(DirEntry {
                                       attr: attr,
                                       path: path,
                                   });
                    }
                }
                Some(Err(e)) => return Err(e),
                None => break,
            }
        }
        *self.dents.borrow_mut() = Some(Rc::new(dents));
        Ok(())
    }
}

impl fs::Dir for Dir {
    fn open(&self) -> Result<Box<Iterator<Item = Result<fs::Entry>>>> {
        self.update_cache()?;
        Ok(Box::new(DirHandler::open(self)))
    }

    fn lookup(&self, name: &OsStr) -> Result<fs::Entry> {
        self.update_cache()?;
        let lookup_path = self.path.join(name);
        for e in self.dents.borrow().as_ref().unwrap().iter() {
            if e.path == lookup_path {
                if e.attr.kind == FileType::Directory {
                    return Ok(fs::Entry::Dir(Box::new(Dir::from_parts(self.archive.clone(),
                                                                      lookup_path.clone(),
                                                                      e.attr,
                                                                      self.dents
                                                                          .borrow()
                                                                          .as_ref()
                                                                          .unwrap()
                                                                          .clone(),
                                                                      self.page_manager.clone()))));
                } else {
                    return Ok(fs::Entry::File(Box::new(CacheFile::new(
                        ArchivedFile::new(
                            self.archive.clone(),
                            e.attr,
                            lookup_path.clone()),
                        self.page_manager.clone()))));
                }
            }
        }
        Err(Error::from_raw_os_error(libc::ENOENT))
    }

    fn getattr(&self) -> Result<FileAttr> {
        if self.attr.borrow().is_none() {
            let mut attr = self.archive.getattr()?;
            attr.kind = FileType::Directory;
            *self.attr.borrow_mut() = Some(attr);
        }
        Ok(self.attr.borrow().unwrap())
    }

    fn name(&self) -> &OsStr {
        if self.path.as_os_str().is_empty() {
            self.archive.name()
        } else {
            self.path.file_name().unwrap()
        }
    }
}

struct DirHandler {
    archive: Rc<Box<fs::File>>,
    path: PathBuf,
    dents: Rc<Vec<DirEntry>>,
    i: usize,
    page_manager: Rc<RefCell<page::PageManager>>,
}

impl DirHandler {
    fn open(dir: &Dir) -> Self {
        DirHandler {
            archive: dir.archive.clone(),
            path: dir.path.clone(),
            dents: dir.dents.borrow().as_ref().unwrap().clone(),
            i: 0,
            page_manager: dir.page_manager.clone(),
        }
    }
}

impl Iterator for DirHandler {
    type Item = Result<fs::Entry>;

    fn next(&mut self) -> Option<Result<fs::Entry>> {
        let dents = self.dents.as_ref();
        while self.i < dents.len() {
            let e = &dents[self.i];
            self.i += 1;
            match e.path.parent() {
                Some(parent) if parent == self.path => {
                    if e.attr.kind == FileType::Directory {
                        return Some(Ok(fs::Entry::Dir(Box::new(Dir::from_parts(self.archive
                                                                               .clone(),
                                                                               e.path.clone(),
                                                                               e.attr,
                                                                               self.dents.clone(),
                                                                               self.page_manager
                                                                               .clone())))));
                    } else {
                        return Some(Ok(
                            fs::Entry::File(Box::new(CacheFile::new(
                            ArchivedFile::new(
                                self.archive.clone(),
                                e.attr,
                                e.path.clone()),
                            self.page_manager.clone())))));
                    }
                }
                _ => continue,
            }
        }
        None
    }
}

pub struct ArchiveViewer {
    page_manager: Rc<RefCell<page::PageManager>>,
}

impl ArchiveViewer {
    pub fn new(max_bytes: usize) -> Result<ArchiveViewer> {
        wrapper::initialize();
        Ok(ArchiveViewer {
               page_manager: Rc::new(RefCell::new(page::PageManager::new(max_bytes)?)),
           })
    }
}

impl fs::Viewer for ArchiveViewer {
    fn view(&self, e: fs::Entry) -> fs::Entry {
        let is_archive = match e {
            fs::Entry::File(ref f) => {
                match Path::new(f.name())
                          .extension()
                          .and_then(|ext| ext.to_str()) {
                    Some(ext) => {
                        match ext.to_lowercase().as_str() {
                            "zip" => true,
                            "rar" => true,
                            _ => false,
                        }
                    }
                    _ => false,
                }
            }
            _ => false,
        };
        if is_archive {
            if let fs::Entry::File(f) = e {
                return fs::Entry::Dir(Box::new(Dir::new(f, self.page_manager.clone())));
            }
        }
        e
    }
}

#[test]
fn test_iterate_dir() {
    use physical;
    use fs::Dir as FSDir;

    let page_manager = Rc::new(RefCell::new(page::PageManager::new(100 * 1024 * 1024).unwrap()));
    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let zip = root.join("assets/test.zip");
    let zip_dir = Dir::new(Box::new(physical::File::new(zip)), page_manager.clone());
    let entries: Vec<_> = zip_dir.open().unwrap().map(|re| re.unwrap()).collect();
    assert!(entries
                .iter()
                .all(|e| e.file_type(0).unwrap() == FileType::RegularFile));
    let mut names: Vec<_> = entries
        .iter()
        .map(|e| PathBuf::from(e.name()))
        .collect();
    names.sort();
    let expect = vec![PathBuf::from("large"), PathBuf::from("small")];
    assert_eq!(names, expect);
}

#[test]
fn test_file_read() {
    use std::fs as stdfs;
    use fs::File;
    use physical;
    use std::io::Read;

    let assets = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("assets");
    let zip = assets.join("test.zip");
    let zip_file = physical::File::new(zip);
    let read_archive = |name| {
        let archive = wrapper::Archive::new(zip_file.open().unwrap());
        let mut r = archive
            .find_open(|e| e.pathname() == PathBuf::from(name))
            .unwrap()
            .unwrap();
        let mut v = Vec::<u8>::new();
        r.read_to_end(&mut v).unwrap();
        v
    };
    let read_file = |name| {
        let mut v = Vec::<u8>::new();
        let mut r = stdfs::File::open(assets.join(name)).unwrap();
        r.read_to_end(&mut v).unwrap();
        v
    };

    let small_actual = read_archive("small");
    let small_expect = read_file("small");
    assert_eq!(small_actual, small_expect);

    let large_actual = read_archive("large");
    let large_expect = read_file("large");
    assert_eq!(large_actual, large_expect);
}
<|endoftext|>


<commit_before>use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

use bit_vec::BitVec;

struct HashIter<'a, T>
where
    T: 'a,
{
    m: usize,
    k: usize,
    i: usize,
    obj: &'a T,
}

impl<'a, T> HashIter<'a, T>
where
    T: 'a,
{
    fn new(m: usize, k: usize, obj: &'a T) -> HashIter<'a, T> {
        HashIter {
            m: m,
            k: k,
            i: 0,
            obj: obj,
        }
    }
}

impl<'a, T> Iterator for HashIter<'a, T>
where
    T: 'a + Hash,
{
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        if self.i < self.k {
            let mut hasher = DefaultHasher::new();
            hasher.write_usize(self.i);
            self.obj.hash(&mut hasher);
            let x = (hasher.finish() as usize) % self.m;

            self.i += 1;

            Some(x)
        } else {
            None
        }
    }
}

/// Simple implementation of a [BloomFilter](https://en.wikipedia.org/wiki/Bloom_filter)
#[derive(Clone)]
pub struct BloomFilter {
    bv: BitVec,
    k: usize,
}

impl BloomFilter {
    /// Create new, empty BloomFilter with internal parameters.
    ///
    /// - `k` is the number of hash functions
    /// - `m` is the number of bits used to store state
    pub fn with_params(m: usize, k: usize) -> BloomFilter {
        BloomFilter {
            bv: BitVec::from_elem(m, false),
            k: k,
        }
    }

    /// Create new, empty BloomFilter with given properties.
    ///
    /// - `n` number of unique elements the BloomFilter is expected to hold, must be `> 0`
    /// - `p` false positive property when querying the BloomFilter after adding `n` unique
    ///   elements, must be `> 0` and `< 1`
    ///
    /// Panics if the parameters are not in range.
    pub fn with_properties(n: usize, p: f64) -> BloomFilter {
        assert!(n > 0);
        assert!(p > 0.);
        assert!(p < 1.);

        let k = (-p.log2()) as usize;
        let ln2 = (2f64).ln();
        let m = (-((n as f64) * p.ln()) / (ln2 * ln2)) as usize;

        BloomFilter::with_params(m, k)
    }

    /// Get `k` (number of hash functions).
    pub fn get_k(&self) -> usize {
        self.k
    }

    /// Get `m` (number of stored bits).
    pub fn get_m(&self) -> usize {
        self.bv.len()
    }

    /// Add new element to the BloomFilter.
    ///
    /// If the same element is added multiple times or if an element results in the same hash
    /// signature, this method does not have any effect.
    pub fn add<T>(&mut self, obj: &T)
    where
        T: Hash,
    {
        for pos in HashIter::new(self.bv.len(), self.k, obj) {
            self.bv.set(pos, true);
        }
    }

    /// Guess if the given element was added to the BloomFilter.
    pub fn query<T>(&self, obj: &T) -> bool
    where
        T: Hash,
    {
        for pos in HashIter::new(self.bv.len(), self.k, obj) {
            if !self.bv.get(pos).unwrap() {
                return false;
            }
        }
        true
    }

    /// Clear state of the BloomFilter, so that it behaves like a fresh one.
    pub fn clear(&mut self) {
        self.bv.clear()
    }

    /// Add the entire content of another bloomfilter to this BloomFilter.
    ///
    /// The result is the same as adding all elements added to `other` to `self` in the first
    /// place.
    ///
    /// Panics if `k` and `m` of the two BloomFilters are not identical.
    pub fn union(&mut self, other: &BloomFilter) {
        assert!(self.k == other.k);
        assert!(self.bv.len() == other.bv.len());
        self.bv.union(&other.bv);
    }

    /// Guess the number of unique elements added to the BloomFilter.
    pub fn guess_n(&self) -> usize {
        let m = self.bv.len() as f64;
        let k = self.k as f64;
        let x = self.bv.iter().filter(|x| *x).count() as f64;

        (-m / k * (1. - x / m).ln()) as usize
    }
}

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

    #[test]
    fn getter() {
        let bf = BloomFilter::with_params(100, 2);
        assert!(bf.get_k() == 2);
        assert!(bf.get_m() == 100);
    }

    #[test]
    fn empty() {
        let bf = BloomFilter::with_params(100, 2);
        assert!(!bf.query(&1));
    }

    #[test]
    fn add() {
        let mut bf = BloomFilter::with_params(100, 2);

        bf.add(&1);
        assert!(bf.query(&1));
        assert!(!bf.query(&2));
    }

    #[test]
    fn clear() {
        let mut bf = BloomFilter::with_params(100, 2);

        bf.add(&1);
        bf.clear();
        assert!(!bf.query(&1));
    }

    #[test]
    fn clone() {
        let mut bf1 = BloomFilter::with_params(100, 2);
        bf1.add(&1);

        let bf2 = bf1.clone();
        bf1.add(&2);
        assert!(bf2.query(&1));
        assert!(!bf2.query(&2));
    }

    #[test]
    fn union() {
        let mut bf1 = BloomFilter::with_params(100, 2);
        bf1.add(&1);
        assert!(bf1.query(&1));
        assert!(!bf1.query(&2));
        assert!(!bf1.query(&3));

        let mut bf2 = BloomFilter::with_params(100, 2);
        bf2.add(&2);
        assert!(!bf2.query(&1));
        assert!(bf2.query(&2));
        assert!(!bf2.query(&3));

        bf1.union(&bf2);
        assert!(bf1.query(&1));
        assert!(bf1.query(&2));
        assert!(!bf1.query(&3));
    }

    #[test]
    fn with_properties() {
        let bf = BloomFilter::with_properties(1000, 0.1);
        assert!(bf.get_k() == 3);
        assert!(bf.get_m() == 4792);
    }

    #[test]
    fn guess_n() {
        let mut bf = BloomFilter::with_params(100, 2);
        assert!(bf.guess_n() == 0);

        bf.add(&1);
        assert!(bf.guess_n() == 1);

        bf.add(&1);
        assert!(bf.guess_n() == 1);

        bf.add(&2);
        assert!(bf.guess_n() == 2);
    }
}
<commit_msg>bloomfilter: impl debug<commit_after>use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{Hash, Hasher};

use bit_vec::BitVec;

struct HashIter<'a, T>
where
    T: 'a,
{
    m: usize,
    k: usize,
    i: usize,
    obj: &'a T,
}

impl<'a, T> HashIter<'a, T>
where
    T: 'a,
{
    fn new(m: usize, k: usize, obj: &'a T) -> HashIter<'a, T> {
        HashIter {
            m: m,
            k: k,
            i: 0,
            obj: obj,
        }
    }
}

impl<'a, T> Iterator for HashIter<'a, T>
where
    T: 'a + Hash,
{
    type Item = usize;

    fn next(&mut self) -> Option<usize> {
        if self.i < self.k {
            let mut hasher = DefaultHasher::new();
            hasher.write_usize(self.i);
            self.obj.hash(&mut hasher);
            let x = (hasher.finish() as usize) % self.m;

            self.i += 1;

            Some(x)
        } else {
            None
        }
    }
}

/// Simple implementation of a [BloomFilter](https://en.wikipedia.org/wiki/Bloom_filter)
#[derive(Clone)]
pub struct BloomFilter {
    bv: BitVec,
    k: usize,
}

impl BloomFilter {
    /// Create new, empty BloomFilter with internal parameters.
    ///
    /// - `k` is the number of hash functions
    /// - `m` is the number of bits used to store state
    pub fn with_params(m: usize, k: usize) -> BloomFilter {
        BloomFilter {
            bv: BitVec::from_elem(m, false),
            k: k,
        }
    }

    /// Create new, empty BloomFilter with given properties.
    ///
    /// - `n` number of unique elements the BloomFilter is expected to hold, must be `> 0`
    /// - `p` false positive property when querying the BloomFilter after adding `n` unique
    ///   elements, must be `> 0` and `< 1`
    ///
    /// Panics if the parameters are not in range.
    pub fn with_properties(n: usize, p: f64) -> BloomFilter {
        assert!(n > 0);
        assert!(p > 0.);
        assert!(p < 1.);

        let k = (-p.log2()) as usize;
        let ln2 = (2f64).ln();
        let m = (-((n as f64) * p.ln()) / (ln2 * ln2)) as usize;

        BloomFilter::with_params(m, k)
    }

    /// Get `k` (number of hash functions).
    pub fn get_k(&self) -> usize {
        self.k
    }

    /// Get `m` (number of stored bits).
    pub fn get_m(&self) -> usize {
        self.bv.len()
    }

    /// Add new element to the BloomFilter.
    ///
    /// If the same element is added multiple times or if an element results in the same hash
    /// signature, this method does not have any effect.
    pub fn add<T>(&mut self, obj: &T)
    where
        T: Hash,
    {
        for pos in HashIter::new(self.bv.len(), self.k, obj) {
            self.bv.set(pos, true);
        }
    }

    /// Guess if the given element was added to the BloomFilter.
    pub fn query<T>(&self, obj: &T) -> bool
    where
        T: Hash,
    {
        for pos in HashIter::new(self.bv.len(), self.k, obj) {
            if !self.bv.get(pos).unwrap() {
                return false;
            }
        }
        true
    }

    /// Clear state of the BloomFilter, so that it behaves like a fresh one.
    pub fn clear(&mut self) {
        self.bv.clear()
    }

    /// Add the entire content of another bloomfilter to this BloomFilter.
    ///
    /// The result is the same as adding all elements added to `other` to `self` in the first
    /// place.
    ///
    /// Panics if `k` and `m` of the two BloomFilters are not identical.
    pub fn union(&mut self, other: &BloomFilter) {
        assert!(self.k == other.k);
        assert!(self.bv.len() == other.bv.len());
        self.bv.union(&other.bv);
    }

    /// Guess the number of unique elements added to the BloomFilter.
    pub fn guess_n(&self) -> usize {
        let m = self.bv.len() as f64;
        let k = self.k as f64;
        let x = self.bv.iter().filter(|x| *x).count() as f64;

        (-m / k * (1. - x / m).ln()) as usize
    }
}

impl fmt::Debug for BloomFilter {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "BloomFilter {{ m: {}, k: {} }}", self.bv.len(), self.k)
    }
}

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

    #[test]
    fn getter() {
        let bf = BloomFilter::with_params(100, 2);
        assert!(bf.get_k() == 2);
        assert!(bf.get_m() == 100);
    }

    #[test]
    fn empty() {
        let bf = BloomFilter::with_params(100, 2);
        assert!(!bf.query(&1));
    }

    #[test]
    fn add() {
        let mut bf = BloomFilter::with_params(100, 2);

        bf.add(&1);
        assert!(bf.query(&1));
        assert!(!bf.query(&2));
    }

    #[test]
    fn clear() {
        let mut bf = BloomFilter::with_params(100, 2);

        bf.add(&1);
        bf.clear();
        assert!(!bf.query(&1));
    }

    #[test]
    fn clone() {
        let mut bf1 = BloomFilter::with_params(100, 2);
        bf1.add(&1);

        let bf2 = bf1.clone();
        bf1.add(&2);
        assert!(bf2.query(&1));
        assert!(!bf2.query(&2));
    }

    #[test]
    fn union() {
        let mut bf1 = BloomFilter::with_params(100, 2);
        bf1.add(&1);
        assert!(bf1.query(&1));
        assert!(!bf1.query(&2));
        assert!(!bf1.query(&3));

        let mut bf2 = BloomFilter::with_params(100, 2);
        bf2.add(&2);
        assert!(!bf2.query(&1));
        assert!(bf2.query(&2));
        assert!(!bf2.query(&3));

        bf1.union(&bf2);
        assert!(bf1.query(&1));
        assert!(bf1.query(&2));
        assert!(!bf1.query(&3));
    }

    #[test]
    fn with_properties() {
        let bf = BloomFilter::with_properties(1000, 0.1);
        assert!(bf.get_k() == 3);
        assert!(bf.get_m() == 4792);
    }

    #[test]
    fn guess_n() {
        let mut bf = BloomFilter::with_params(100, 2);
        assert!(bf.guess_n() == 0);

        bf.add(&1);
        assert!(bf.guess_n() == 1);

        bf.add(&1);
        assert!(bf.guess_n() == 1);

        bf.add(&2);
        assert!(bf.guess_n() == 2);
    }

    #[test]
    fn debug() {
        let bf = BloomFilter::with_params(100, 2);
        assert!(format!("{:?}", bf) == "BloomFilter { m: 100, k: 2 }");
    }
}
<|endoftext|>


<commit_before>mod types;
mod parser;
mod metadata;

pub use self::types::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

pub use self::parser::metadata_parser;

pub use self::metadata::{
  get_stream_info, get_vorbis_comment, get_cue_sheet, get_picture,
};
<commit_msg>Add module level description to metadata module<commit_after>//! Provides an interface for dealing with FLAC metadata blocks.

mod types;
mod parser;
mod metadata;

pub use self::types::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

pub use self::parser::metadata_parser;

pub use self::metadata::{
  get_stream_info, get_vorbis_comment, get_cue_sheet, get_picture,
};
<|endoftext|>


<commit_before>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use spinwait::SpinWait;
use std::cell::Cell;
use std::mem;
#[cfg(not(has_localkey_try_with))]
use std::panic;
use std::ptr;
use std::sync::atomic::{fence, AtomicUsize, Ordering};
use std::thread::LocalKey;
use thread_parker::ThreadParker;

struct ThreadData {
    parker: ThreadParker,

    // Linked list of threads in the queue. The queue is split into two parts:
    // the processed part and the unprocessed part. When new nodes are added to
    // the list, they only have the next pointer set, and queue_tail is null.
    //
    // Nodes are processed with the queue lock held, which consists of setting
    // the prev pointer for each node and setting the queue_tail pointer on the
    // first processed node of the list.
    //
    // This setup allows nodes to be added to the queue without a lock, while
    // still allowing O(1) removal of nodes from the processed part of the list.
    // The only cost is the O(n) processing, but this only needs to be done
    // once for each node, and therefore isn't too expensive.
    queue_tail: Cell<*const ThreadData>,
    prev: Cell<*const ThreadData>,
    next: Cell<*const ThreadData>,
}

impl ThreadData {
    fn new() -> ThreadData {
        ThreadData {
            parker: ThreadParker::new(),
            queue_tail: Cell::new(ptr::null()),
            prev: Cell::new(ptr::null()),
            next: Cell::new(ptr::null()),
        }
    }
}

// Returns a ThreadData structure for the current thread
fn get_thread_data(local: &mut Option<ThreadData>) -> &ThreadData {
    // Try to read from thread-local storage, but return None if the TLS has
    // already been destroyed.
    #[cfg(has_localkey_try_with)]
    fn try_get_tls(key: &'static LocalKey<ThreadData>) -> Option<*const ThreadData> {
        key.try_with(|x| x as *const ThreadData).ok()
    }
    #[cfg(not(has_localkey_try_with))]
    fn try_get_tls(key: &'static LocalKey<ThreadData>) -> Option<*const ThreadData> {
        panic::catch_unwind(|| key.with(|x| x as *const ThreadData)).ok()
    }

    // If ThreadData is expensive to construct, then we want to use a cached
    // version in thread-local storage if possible.
    if !cfg!(windows) && !cfg!(all(feature = "nightly", target_os = "linux")) {
        thread_local!(static THREAD_DATA: ThreadData = ThreadData::new());
        if let Some(tls) = try_get_tls(&THREAD_DATA) {
            return unsafe { &*tls };
        }
    }

    // Otherwise just create a ThreadData on the stack
    *local = Some(ThreadData::new());
    local.as_ref().unwrap()
}

const LOCKED_BIT: usize = 1;
const QUEUE_LOCKED_BIT: usize = 2;
const QUEUE_MASK: usize = !3;

// Word-sized lock that is used to implement the parking_lot API. Since this
// can't use parking_lot, it instead manages its own queue of waiting threads.
pub struct WordLock {
    state: AtomicUsize,
}

impl WordLock {
    #[inline]
    pub fn new() -> WordLock {
        WordLock {
            state: AtomicUsize::new(0),
        }
    }

    #[inline]
    pub unsafe fn lock(&self) {
        if self
            .state
            .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
            .is_ok()
        {
            return;
        }
        self.lock_slow();
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let state = self.state.fetch_sub(LOCKED_BIT, Ordering::Release);
        if state.is_queue_locked() || state.queue_head().is_null() {
            return;
        }
        self.unlock_slow();
    }

    #[cold]
    #[inline(never)]
    unsafe fn lock_slow(&self) {
        let mut spinwait = SpinWait::new();
        let mut state = self.state.load(Ordering::Relaxed);
        loop {
            // Grab the lock if it isn't locked, even if there is a queue on it
            if !state.is_locked() {
                match self.state.compare_exchange_weak(
                    state,
                    state | LOCKED_BIT,
                    Ordering::Acquire,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => return,
                    Err(x) => state = x,
                }
                continue;
            }

            // If there is no queue, try spinning a few times
            if state.queue_head().is_null() && spinwait.spin() {
                state = self.state.load(Ordering::Relaxed);
                continue;
            }

            // Get our thread data and prepare it for parking
            let mut thread_data = None;
            let thread_data = get_thread_data(&mut thread_data);
            assert!(mem::align_of_val(thread_data) > !QUEUE_MASK);
            thread_data.parker.prepare_park();

            // Add our thread to the front of the queue
            let queue_head = state.queue_head();
            if queue_head.is_null() {
                thread_data.queue_tail.set(thread_data);
                thread_data.prev.set(ptr::null());
            } else {
                thread_data.queue_tail.set(ptr::null());
                thread_data.prev.set(ptr::null());
                thread_data.next.set(queue_head);
            }
            if let Err(x) = self.state.compare_exchange_weak(
                state,
                state.with_queue_head(thread_data),
                Ordering::Release,
                Ordering::Relaxed,
            ) {
                state = x;
                continue;
            }

            // Sleep until we are woken up by an unlock
            thread_data.parker.park();

            // Loop back and try locking again
            spinwait.reset();
            self.state.load(Ordering::Relaxed);
        }
    }

    #[cold]
    #[inline(never)]
    unsafe fn unlock_slow(&self) {
        let mut state = self.state.load(Ordering::Relaxed);
        loop {
            // We just unlocked the WordLock. Just check if there is a thread
            // to wake up. If the queue is locked then another thread is already
            // taking care of waking up a thread.
            if state.is_queue_locked() || state.queue_head().is_null() {
                return;
            }

            // Try to grab the queue lock
            match self.state.compare_exchange_weak(
                state,
                state | QUEUE_LOCKED_BIT,
                Ordering::Acquire,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(x) => state = x,
            }
        }

        // Now we have the queue lock and the queue is non-empty
        'outer: loop {
            // First, we need to fill in the prev pointers for any newly added
            // threads. We do this until we reach a node that we previously
            // processed, which has a non-null queue_tail pointer.
            let queue_head = state.queue_head();
            let mut queue_tail;
            let mut current = queue_head;
            loop {
                queue_tail = (*current).queue_tail.get();
                if !queue_tail.is_null() {
                    break;
                }
                let next = (*current).next.get();
                (*next).prev.set(current);
                current = next;
            }

            // Set queue_tail on the queue head to indicate that the whole list
            // has prev pointers set correctly.
            (*queue_head).queue_tail.set(queue_tail);

            // If the WordLock is locked, then there is no point waking up a
            // thread now. Instead we let the next unlocker take care of waking
            // up a thread.
            if state.is_locked() {
                match self.state.compare_exchange_weak(
                    state,
                    state & !QUEUE_LOCKED_BIT,
                    Ordering::Release,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => return,
                    Err(x) => state = x,
                }

                // Need an acquire fence before reading the new queue
                fence(Ordering::Acquire);
                continue;
            }

            // Remove the last thread from the queue and unlock the queue
            let new_tail = (*queue_tail).prev.get();
            if new_tail.is_null() {
                loop {
                    match self.state.compare_exchange_weak(
                        state,
                        state & LOCKED_BIT,
                        Ordering::Release,
                        Ordering::Relaxed,
                    ) {
                        Ok(_) => break,
                        Err(x) => state = x,
                    }

                    // If the compare_exchange failed because a new thread was
                    // added to the queue then we need to re-scan the queue to
                    // find the previous element.
                    if state.queue_head().is_null() {
                        continue;
                    } else {
                        // Need an acquire fence before reading the new queue
                        fence(Ordering::Acquire);
                        continue 'outer;
                    }
                }
            } else {
                (*queue_head).queue_tail.set(new_tail);
                self.state.fetch_and(!QUEUE_LOCKED_BIT, Ordering::Release);
            }

            // Finally, wake up the thread we removed from the queue. Note that
            // we don't need to worry about any races here since the thread is
            // guaranteed to be sleeping right now and we are the only one who
            // can wake it up.
            (*queue_tail).parker.unpark_lock().unpark();
            break;
        }
    }
}

trait LockState {
    fn is_locked(self) -> bool;
    fn is_queue_locked(self) -> bool;
    fn queue_head(self) -> *const ThreadData;
    fn with_queue_head(self, thread_data: *const ThreadData) -> Self;
}

impl LockState for usize {
    #[inline]
    fn is_locked(self) -> bool {
        self & LOCKED_BIT != 0
    }

    #[inline]
    fn is_queue_locked(self) -> bool {
        self & QUEUE_LOCKED_BIT != 0
    }

    #[inline]
    fn queue_head(self) -> *const ThreadData {
        (self & QUEUE_MASK) as *const ThreadData
    }

    #[inline]
    fn with_queue_head(self, thread_data: *const ThreadData) -> Self {
        (self & !QUEUE_MASK) | thread_data as *const _ as usize
    }
}
<commit_msg>Mark WordLock::lock as not unsafe<commit_after>// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use spinwait::SpinWait;
use std::cell::Cell;
use std::mem;
#[cfg(not(has_localkey_try_with))]
use std::panic;
use std::ptr;
use std::sync::atomic::{fence, AtomicUsize, Ordering};
use std::thread::LocalKey;
use thread_parker::ThreadParker;

struct ThreadData {
    parker: ThreadParker,

    // Linked list of threads in the queue. The queue is split into two parts:
    // the processed part and the unprocessed part. When new nodes are added to
    // the list, they only have the next pointer set, and queue_tail is null.
    //
    // Nodes are processed with the queue lock held, which consists of setting
    // the prev pointer for each node and setting the queue_tail pointer on the
    // first processed node of the list.
    //
    // This setup allows nodes to be added to the queue without a lock, while
    // still allowing O(1) removal of nodes from the processed part of the list.
    // The only cost is the O(n) processing, but this only needs to be done
    // once for each node, and therefore isn't too expensive.
    queue_tail: Cell<*const ThreadData>,
    prev: Cell<*const ThreadData>,
    next: Cell<*const ThreadData>,
}

impl ThreadData {
    fn new() -> ThreadData {
        ThreadData {
            parker: ThreadParker::new(),
            queue_tail: Cell::new(ptr::null()),
            prev: Cell::new(ptr::null()),
            next: Cell::new(ptr::null()),
        }
    }
}

// Returns a ThreadData structure for the current thread
fn get_thread_data(local: &mut Option<ThreadData>) -> &ThreadData {
    // Try to read from thread-local storage, but return None if the TLS has
    // already been destroyed.
    #[cfg(has_localkey_try_with)]
    fn try_get_tls(key: &'static LocalKey<ThreadData>) -> Option<*const ThreadData> {
        key.try_with(|x| x as *const ThreadData).ok()
    }
    #[cfg(not(has_localkey_try_with))]
    fn try_get_tls(key: &'static LocalKey<ThreadData>) -> Option<*const ThreadData> {
        panic::catch_unwind(|| key.with(|x| x as *const ThreadData)).ok()
    }

    // If ThreadData is expensive to construct, then we want to use a cached
    // version in thread-local storage if possible.
    if !cfg!(windows) && !cfg!(all(feature = "nightly", target_os = "linux")) {
        thread_local!(static THREAD_DATA: ThreadData = ThreadData::new());
        if let Some(tls) = try_get_tls(&THREAD_DATA) {
            return unsafe { &*tls };
        }
    }

    // Otherwise just create a ThreadData on the stack
    *local = Some(ThreadData::new());
    local.as_ref().unwrap()
}

const LOCKED_BIT: usize = 1;
const QUEUE_LOCKED_BIT: usize = 2;
const QUEUE_MASK: usize = !3;

// Word-sized lock that is used to implement the parking_lot API. Since this
// can't use parking_lot, it instead manages its own queue of waiting threads.
pub struct WordLock {
    state: AtomicUsize,
}

impl WordLock {
    #[inline]
    pub fn new() -> WordLock {
        WordLock {
            state: AtomicUsize::new(0),
        }
    }

    #[inline]
    pub fn lock(&self) {
        if self
            .state
            .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed)
            .is_ok()
        {
            return;
        }
        self.lock_slow();
    }

    #[inline]
    pub unsafe fn unlock(&self) {
        let state = self.state.fetch_sub(LOCKED_BIT, Ordering::Release);
        if state.is_queue_locked() || state.queue_head().is_null() {
            return;
        }
        self.unlock_slow();
    }

    #[cold]
    #[inline(never)]
    fn lock_slow(&self) {
        let mut spinwait = SpinWait::new();
        let mut state = self.state.load(Ordering::Relaxed);
        loop {
            // Grab the lock if it isn't locked, even if there is a queue on it
            if !state.is_locked() {
                match self.state.compare_exchange_weak(
                    state,
                    state | LOCKED_BIT,
                    Ordering::Acquire,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => return,
                    Err(x) => state = x,
                }
                continue;
            }

            // If there is no queue, try spinning a few times
            if state.queue_head().is_null() && spinwait.spin() {
                state = self.state.load(Ordering::Relaxed);
                continue;
            }

            // Get our thread data and prepare it for parking
            let mut thread_data = None;
            let thread_data = get_thread_data(&mut thread_data);
            assert!(mem::align_of_val(thread_data) > !QUEUE_MASK);
            unsafe { thread_data.parker.prepare_park(); }

            // Add our thread to the front of the queue
            let queue_head = state.queue_head();
            if queue_head.is_null() {
                thread_data.queue_tail.set(thread_data);
                thread_data.prev.set(ptr::null());
            } else {
                thread_data.queue_tail.set(ptr::null());
                thread_data.prev.set(ptr::null());
                thread_data.next.set(queue_head);
            }
            if let Err(x) = self.state.compare_exchange_weak(
                state,
                state.with_queue_head(thread_data),
                Ordering::Release,
                Ordering::Relaxed,
            ) {
                state = x;
                continue;
            }

            // Sleep until we are woken up by an unlock
            unsafe { thread_data.parker.park(); }

            // Loop back and try locking again
            spinwait.reset();
            self.state.load(Ordering::Relaxed);
        }
    }

    #[cold]
    #[inline(never)]
    unsafe fn unlock_slow(&self) {
        let mut state = self.state.load(Ordering::Relaxed);
        loop {
            // We just unlocked the WordLock. Just check if there is a thread
            // to wake up. If the queue is locked then another thread is already
            // taking care of waking up a thread.
            if state.is_queue_locked() || state.queue_head().is_null() {
                return;
            }

            // Try to grab the queue lock
            match self.state.compare_exchange_weak(
                state,
                state | QUEUE_LOCKED_BIT,
                Ordering::Acquire,
                Ordering::Relaxed,
            ) {
                Ok(_) => break,
                Err(x) => state = x,
            }
        }

        // Now we have the queue lock and the queue is non-empty
        'outer: loop {
            // First, we need to fill in the prev pointers for any newly added
            // threads. We do this until we reach a node that we previously
            // processed, which has a non-null queue_tail pointer.
            let queue_head = state.queue_head();
            let mut queue_tail;
            let mut current = queue_head;
            loop {
                queue_tail = (*current).queue_tail.get();
                if !queue_tail.is_null() {
                    break;
                }
                let next = (*current).next.get();
                (*next).prev.set(current);
                current = next;
            }

            // Set queue_tail on the queue head to indicate that the whole list
            // has prev pointers set correctly.
            (*queue_head).queue_tail.set(queue_tail);

            // If the WordLock is locked, then there is no point waking up a
            // thread now. Instead we let the next unlocker take care of waking
            // up a thread.
            if state.is_locked() {
                match self.state.compare_exchange_weak(
                    state,
                    state & !QUEUE_LOCKED_BIT,
                    Ordering::Release,
                    Ordering::Relaxed,
                ) {
                    Ok(_) => return,
                    Err(x) => state = x,
                }

                // Need an acquire fence before reading the new queue
                fence(Ordering::Acquire);
                continue;
            }

            // Remove the last thread from the queue and unlock the queue
            let new_tail = (*queue_tail).prev.get();
            if new_tail.is_null() {
                loop {
                    match self.state.compare_exchange_weak(
                        state,
                        state & LOCKED_BIT,
                        Ordering::Release,
                        Ordering::Relaxed,
                    ) {
                        Ok(_) => break,
                        Err(x) => state = x,
                    }

                    // If the compare_exchange failed because a new thread was
                    // added to the queue then we need to re-scan the queue to
                    // find the previous element.
                    if state.queue_head().is_null() {
                        continue;
                    } else {
                        // Need an acquire fence before reading the new queue
                        fence(Ordering::Acquire);
                        continue 'outer;
                    }
                }
            } else {
                (*queue_head).queue_tail.set(new_tail);
                self.state.fetch_and(!QUEUE_LOCKED_BIT, Ordering::Release);
            }

            // Finally, wake up the thread we removed from the queue. Note that
            // we don't need to worry about any races here since the thread is
            // guaranteed to be sleeping right now and we are the only one who
            // can wake it up.
            (*queue_tail).parker.unpark_lock().unpark();
            break;
        }
    }
}

trait LockState {
    fn is_locked(self) -> bool;
    fn is_queue_locked(self) -> bool;
    fn queue_head(self) -> *const ThreadData;
    fn with_queue_head(self, thread_data: *const ThreadData) -> Self;
}

impl LockState for usize {
    #[inline]
    fn is_locked(self) -> bool {
        self & LOCKED_BIT != 0
    }

    #[inline]
    fn is_queue_locked(self) -> bool {
        self & QUEUE_LOCKED_BIT != 0
    }

    #[inline]
    fn queue_head(self) -> *const ThreadData {
        (self & QUEUE_MASK) as *const ThreadData
    }

    #[inline]
    fn with_queue_head(self, thread_data: *const ThreadData) -> Self {
        (self & !QUEUE_MASK) | thread_data as *const _ as usize
    }
}
<|endoftext|>


<commit_before>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use hyper::{Method, StatusCode, Headers};
use hyper::server::{Request, Response};

use ::APP_CONF;
use ::APP_CACHE_STORE;
use header::response_ignore::HeaderResponseBloomResponseIgnore;
use header::response_ttl::HeaderResponseBloomResponseTTL;

pub struct CacheWrite;

impl CacheWrite {
    pub fn save(key: &str, req: Request, res: Response) -> bool {
        let method = req.method();
        let status = res.status();
        let headers = res.headers();

        if Self::is_cacheable(&method, &status, &headers)
            == true {
            // Acquire TTL from response, or fallback to default TTL
            let ttl = match headers.get::<HeaderResponseBloomResponseTTL>() {
                None => APP_CONF.cache.ttl_default,
                Some(value) => value.0
            };

            // TODO: nuke contextual / time-dependant headers, add only \
            //   static headers
            // TODO: append body
            let value = "";

            // Write to cache
            APP_CACHE_STORE.set(key, value, ttl).is_ok()
        } else {
            // Not cacheable, ignore
            false
        }
    }

    fn is_cacheable(method: &Method, status: &StatusCode, headers: &Headers)
        -> bool {
        Self::is_cacheable_method(method) == true &&
            Self::is_cacheable_status(status) == true &&
            Self::is_cacheable_response(headers) == true
    }

    fn is_cacheable_method(method: &Method) -> bool {
        match *method {
            Method::Get | Method::Head => true,
            _ => false
        }
    }

    fn is_cacheable_status(status: &StatusCode) -> bool {
        match *status {
            StatusCode::Ok
            | StatusCode::NonAuthoritativeInformation
            | StatusCode::NoContent
            | StatusCode::ResetContent
            | StatusCode::PartialContent
            | StatusCode::MultiStatus
            | StatusCode::AlreadyReported
            | StatusCode::MultipleChoices
            | StatusCode::MovedPermanently
            | StatusCode::Found
            | StatusCode::SeeOther
            | StatusCode::PermanentRedirect
            | StatusCode::Unauthorized
            | StatusCode::PaymentRequired
            | StatusCode::Forbidden
            | StatusCode::NotFound
            | StatusCode::MethodNotAllowed
            | StatusCode::Gone
            | StatusCode::UriTooLong
            | StatusCode::Locked
            | StatusCode::FailedDependency
            | StatusCode::NotImplemented => {
                true
            }
            _ => {
                false
            }
        }
    }

    fn is_cacheable_response(headers: &Headers) -> bool {
        // Ignore responses with 'Bloom-Response-Ignore'
        headers.has::<HeaderResponseBloomResponseIgnore>()
    }
}

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

    #[test]
    fn it_asserts_valid_cacheable_method() {
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Get),
            true, "GET");
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Head),
            true, "HEAD");
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Options),
            false, "OPTIONS");
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Post),
            false, "POST");
    }

    #[test]
    fn it_asserts_valid_cacheable_status() {
        assert_eq!(CacheWrite::is_cacheable_status(StatusCode::Ok),
            true, "200 OK");
        assert_eq!(CacheWrite::is_cacheable_status(StatusCode::Unauthorized),
            true, "401 OK");
        assert_eq!(CacheWrite::is_cacheable_status(StatusCode::BadRequest),
            false, "400 Bad Request");
        assert_eq!(CacheWrite::is_cacheable_status(StatusCode::InternalServerError),
            false, "500 Internal Server Error");
    }
}
<commit_msg>Fix test<commit_after>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use hyper::{Method, StatusCode, Headers};
use hyper::server::{Request, Response};

use ::APP_CONF;
use ::APP_CACHE_STORE;
use header::response_ignore::HeaderResponseBloomResponseIgnore;
use header::response_ttl::HeaderResponseBloomResponseTTL;

pub struct CacheWrite;

impl CacheWrite {
    pub fn save(key: &str, req: Request, res: Response) -> bool {
        let method = req.method();
        let status = res.status();
        let headers = res.headers();

        if Self::is_cacheable(&method, &status, &headers)
            == true {
            // Acquire TTL from response, or fallback to default TTL
            let ttl = match headers.get::<HeaderResponseBloomResponseTTL>() {
                None => APP_CONF.cache.ttl_default,
                Some(value) => value.0
            };

            // TODO: nuke contextual / time-dependant headers, add only \
            //   static headers
            // TODO: append body
            let value = "";

            // Write to cache
            APP_CACHE_STORE.set(key, value, ttl).is_ok()
        } else {
            // Not cacheable, ignore
            false
        }
    }

    fn is_cacheable(method: &Method, status: &StatusCode, headers: &Headers)
        -> bool {
        Self::is_cacheable_method(method) == true &&
            Self::is_cacheable_status(status) == true &&
            Self::is_cacheable_response(headers) == true
    }

    fn is_cacheable_method(method: &Method) -> bool {
        match *method {
            Method::Get | Method::Head => true,
            _ => false
        }
    }

    fn is_cacheable_status(status: &StatusCode) -> bool {
        match *status {
            StatusCode::Ok
            | StatusCode::NonAuthoritativeInformation
            | StatusCode::NoContent
            | StatusCode::ResetContent
            | StatusCode::PartialContent
            | StatusCode::MultiStatus
            | StatusCode::AlreadyReported
            | StatusCode::MultipleChoices
            | StatusCode::MovedPermanently
            | StatusCode::Found
            | StatusCode::SeeOther
            | StatusCode::PermanentRedirect
            | StatusCode::Unauthorized
            | StatusCode::PaymentRequired
            | StatusCode::Forbidden
            | StatusCode::NotFound
            | StatusCode::MethodNotAllowed
            | StatusCode::Gone
            | StatusCode::UriTooLong
            | StatusCode::Locked
            | StatusCode::FailedDependency
            | StatusCode::NotImplemented => {
                true
            }
            _ => {
                false
            }
        }
    }

    fn is_cacheable_response(headers: &Headers) -> bool {
        // Ignore responses with 'Bloom-Response-Ignore'
        headers.has::<HeaderResponseBloomResponseIgnore>()
    }
}

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

    #[test]
    fn it_asserts_valid_cacheable_method() {
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Get),
            true, "GET");
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Head),
            true, "HEAD");
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Options),
            false, "OPTIONS");
        assert_eq!(CacheWrite::is_cacheable_method(&Method::Post),
            false, "POST");
    }

    #[test]
    fn it_asserts_valid_cacheable_status() {
        assert_eq!(CacheWrite::is_cacheable_status(&StatusCode::Ok),
            true, "200 OK");
        assert_eq!(CacheWrite::is_cacheable_status(&StatusCode::Unauthorized),
            true, "401 OK");
        assert_eq!(CacheWrite::is_cacheable_status(&StatusCode::BadRequest),
            false, "400 Bad Request");
        assert_eq!(CacheWrite::is_cacheable_status(&StatusCode::InternalServerError),
            false, "500 Internal Server Error");
    }
}
<|endoftext|>


<commit_before>extern crate log_domain;
extern crate num_traits;
extern crate rustomata;

use log_domain::LogDomain;
use std::fs::File;
use std::io::Read;
use std::marker::PhantomData;

use rustomata::approximation::ApproximationStrategy;
use rustomata::approximation::equivalence_classes::EquivalenceRelation;
use rustomata::approximation::relabel::RlbElement;
use rustomata::cfg::*;
use rustomata::push_down_automaton::*;
use rustomata::recognisable::*;

fn cfg_from_file(grammar_file_path: &str) -> CFG<String, String, LogDomain<f64>>
{
    let mut grammar_file = File::open(grammar_file_path).unwrap();
    let mut grammar_string = String::new();
    let _ = grammar_file.read_to_string(&mut grammar_string);
    grammar_string.parse().unwrap()
}

fn example_equivalence_relation() -> EquivalenceRelation<String, String> {
    let mut relation_file = File::open("examples/example.classes").unwrap();
    let mut relation_string = String::new();
    let _ = relation_file.read_to_string(&mut relation_string);

    relation_string.parse().unwrap()
}

#[test]
fn test_relabel_pushdown_correctness() {
    let automaton = PushDownAutomaton::from(cfg_from_file("examples/example2.cfg"));
    let rel = example_equivalence_relation();
    let mapping = |ps: &PushState<_, _>| ps.map(|nt| rel.project(nt));
    let rlb = RlbElement::new(&mapping);
    let (relabelled_automaton, _) = rlb.approximate_automaton(&automaton);

    let true_positives_and_true_negatives = vec![
        "aab",
        "bba",
        "aaabb",
        "aabba",
        "",
        "aa",
        "aaab",
        "bbbbbb",
    ];

    for word in true_positives_and_true_negatives {
        let input: Vec<_> = String::from(word).chars().map(|x| x.to_string()).collect();
        assert_eq!(
            automaton.recognise(input.clone()).next().is_some(),
            relabelled_automaton.recognise(input).next().is_some()
        );
    }

    let false_positives = vec![
        "aaa",
        "bbb",
        "aabaa",
        "abaaa",
    ];

    for word in false_positives {
        let input: Vec<_> = String::from(word).chars().map(|x| x.to_string()).collect();
        assert_eq!(false, automaton.recognise(input.clone()).next().is_some());
        assert_eq!(true, relabelled_automaton.recognise(input).next().is_some());
    }
}

#[test]
fn test_cfg_from_str_correctness() {
    let rule_s0 = CFGRule {
        head: String::from("S"),
        composition: CFGComposition { composition: vec![
            LetterT::Value(String::from("a")),
            LetterT::Label(String::from("S")),
            LetterT::Value(String::from("b")),
        ] },
        weight: LogDomain::new(0.4).unwrap()
    };
    let rule_s1 = CFGRule {
        head: String::from("S"),
        composition: CFGComposition { composition: vec![]},
        weight: LogDomain::new(0.6).unwrap()
    };
    let control_grammar = CFG {
        _dummy: PhantomData,
        initial: vec![String::from("S")],
        rules: vec![rule_s0, rule_s1]
    };

    let grammar = cfg_from_file("examples/example.cfg");
    assert_eq!(
        control_grammar.clone(),
        grammar.clone()
    );

    let control_automaton = PushDownAutomaton::from(control_grammar);
    let automaton = PushDownAutomaton::from(grammar);
    let words = vec![
        "",
        "aabb",
        "abb",
        "aab",
    ];

    for word in words {
        let input: Vec<_> = String::from(word).chars().map(|x| x.to_string()).collect();
        assert_eq!(
            control_automaton.recognise(input.clone()).next().is_some(),
            automaton.recognise(input).next().is_some()
        );
    }
}
<commit_msg>tests/cfg.rs: added test for PushDownAutomaton::from_str()<commit_after>extern crate log_domain;
extern crate num_traits;
extern crate rustomata;

use log_domain::LogDomain;
// TODO: Uncomment once PushDownAutomaton::FromStr has been implemented
// use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::marker::PhantomData;

use rustomata::approximation::ApproximationStrategy;
use rustomata::approximation::equivalence_classes::EquivalenceRelation;
use rustomata::approximation::relabel::RlbElement;
use rustomata::cfg::*;
use rustomata::push_down_automaton::*;
use rustomata::recognisable::*;
// TODO: Uncomment once PushDownAutomaton::FromStr has been implemented
// use rustomata::recognisable::automaton::Automaton;

fn cfg_from_file(grammar_file_path: &str) -> CFG<String, String, LogDomain<f64>>
{
    let mut grammar_file = File::open(grammar_file_path).unwrap();
    let mut grammar_string = String::new();
    let _ = grammar_file.read_to_string(&mut grammar_string);
    grammar_string.parse().unwrap()
}

fn example_equivalence_relation() -> EquivalenceRelation<String, String> {
    let mut relation_file = File::open("examples/example.classes").unwrap();
    let mut relation_string = String::new();
    let _ = relation_file.read_to_string(&mut relation_string);

    relation_string.parse().unwrap()
}

#[test]
fn test_relabel_pushdown_correctness() {
    let automaton = PushDownAutomaton::from(cfg_from_file("examples/example2.cfg"));
    let rel = example_equivalence_relation();
    let mapping = |ps: &PushState<_, _>| ps.map(|nt| rel.project(nt));
    let rlb = RlbElement::new(&mapping);
    let (relabelled_automaton, _) = rlb.approximate_automaton(&automaton);

    let true_positives_and_true_negatives = vec![
        "aab",
        "bba",
        "aaabb",
        "aabba",
        "",
        "aa",
        "aaab",
        "bbbbbb",
    ];

    for word in true_positives_and_true_negatives {
        let input: Vec<_> = String::from(word).chars().map(|x| x.to_string()).collect();
        assert_eq!(
            automaton.recognise(input.clone()).next().is_some(),
            relabelled_automaton.recognise(input).next().is_some()
        );
    }

    let false_positives = vec![
        "aaa",
        "bbb",
        "aabaa",
        "abaaa",
    ];

    for word in false_positives {
        let input: Vec<_> = String::from(word).chars().map(|x| x.to_string()).collect();
        assert_eq!(false, automaton.recognise(input.clone()).next().is_some());
        assert_eq!(true, relabelled_automaton.recognise(input).next().is_some());
    }
}

#[test]
fn test_cfg_from_str_correctness() {
    let rule_s0 = CFGRule {
        head: String::from("S"),
        composition: CFGComposition { composition: vec![
            LetterT::Value(String::from("a")),
            LetterT::Label(String::from("S")),
            LetterT::Value(String::from("b")),
        ] },
        weight: LogDomain::new(0.4).unwrap()
    };
    let rule_s1 = CFGRule {
        head: String::from("S"),
        composition: CFGComposition { composition: vec![]},
        weight: LogDomain::new(0.6).unwrap()
    };
    let control_grammar = CFG {
        _dummy: PhantomData,
        initial: vec![String::from("S")],
        rules: vec![rule_s0, rule_s1]
    };

    let grammar = cfg_from_file("examples/example.cfg");
    assert_eq!(
        control_grammar.clone(),
        grammar.clone()
    );

    let control_automaton = PushDownAutomaton::from(control_grammar);
    let automaton = PushDownAutomaton::from(grammar);
    let words = vec![
        "",
        "aabb",
        "abb",
        "aab",
    ];

    for word in words {
        let input: Vec<_> = String::from(word).chars().map(|x| x.to_string()).collect();
        assert_eq!(
            control_automaton.recognise(input.clone()).next().is_some(),
            automaton.recognise(input).next().is_some()
        );
    }
}

// TODO: Uncomment once PushDownAutomaton::FromStr has been implemented
/*
#[test]
fn test_pushdown_automaton_from_str() {
    use PushDownInstruction::Replace;

    let unprocessed_transitions = vec![
        ("a", Replace { current_val: vec!["(a)".to_string()], new_val: vec![] }, 1.0),
        ("b", Replace { current_val: vec!["(b)".to_string()], new_val: vec![] }, 1.0),
        ("",  Replace { current_val: vec!["(S)".to_string()], new_val: vec![] }, 0.6),
        ("",  Replace { current_val: vec!["(S)".to_string()], new_val: vec![
            "(b)".to_string(), "(S)".to_string(), "(a)".to_string(),
        ] }, 0.4),
        ("",  Replace { current_val: vec!["I".to_string()], new_val: vec!["(S)".to_string()] }, 1.0),
    ];

    let mut transitions = Vec::new();
    for (terminal, instruction, weight) in unprocessed_transitions {
        transitions.push(Transition {
            word: terminal.chars().map(|x| x.to_string()).collect(),
            instruction,
            weight: LogDomain::new(weight).unwrap()
        });
    }
    let control_automaton = PushDownAutomaton::new(transitions, PushDown::new("I".to_string(), "@".to_string()));

    let mut automaton_file = File::open("examples/example.pda").unwrap();
    let mut automaton_string = String::new();
    let _ = automaton_file.read_to_string(&mut automaton_string);
    let automaton: PushDownAutomaton<String, String, LogDomain<f64>> = automaton_string.parse().unwrap();

    assert_eq!(
        control_automaton.initial(),
        automaton.initial()
    );
    assert_eq!(
        control_automaton.list_transitions().collect::<HashSet<_>>(),
        automaton.list_transitions().collect::<HashSet<_>>()
    );
}
*/
<|endoftext|>


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

use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;
use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
use std::cell::{Ref, RefCell};
use std::mem;

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone, Encodable)]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<(uint, uint)>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(list: DOMString) -> AttrValue {
        let mut indexes = vec![];
        let mut last_index: uint = 0;
        for (index, ch) in list.as_slice().char_indices() {
            if HTML_SPACE_CHARACTERS.iter().any(|&space| space == ch) {
                indexes.push((last_index, index));
                last_index = index + 1;
            }
        }
        return TokenListAttrValue(list, indexes);
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

    pub fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[deriving(Encodable)]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: Traceable<RefCell<AttrValue>>,
    pub name: Atom,
    pub namespace: Namespace,
    pub prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: &JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: Traceable::new(RefCell::new(value)),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: &JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: &JSRef<Element>) -> Temporary<Attr> {
        let attr = Attr::new_inherited(local_name, value, name, namespace, prefix, owner);
        reflect_dom_object(box attr, &Window(*window), AttrBinding::Wrap)
    }

    pub fn set_value(&self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: &JSRef<Node> = NodeCast::from_ref(&*owner);
        let namespace_is_null = self.namespace == namespace::Null;

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(node).before_remove_attr(
                        self.local_name(),
                        self.value().as_slice().to_string())
                }
            }
            FirstSetAttr => {}
        }

        *self.value.deref().borrow_mut() = value;

        if namespace_is_null {
            vtable_for(node).after_set_attr(
                self.local_name(),
                self.value().as_slice().to_string())
        }
    }

    pub fn value<'a>(&'a self) -> Ref<'a, AttrValue> {
        self.value.deref().borrow()
    }

    pub fn local_name<'a>(&'a self) -> &'a Atom {
        &self.local_name
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(&self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(&self) -> DOMString {
        self.value().as_slice().to_string()
    }

    fn SetValue(&self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.deref().parse_attribute(
            &self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(&self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(&self) -> Option<DOMString> {
        match self.namespace.to_str() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(&self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
}

impl AttrHelpersForLayout for Attr {
    unsafe fn value_ref_forever(&self) -> &'static str {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        value.as_slice()
    }

    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }
}
<commit_msg>Make AttrValue::as_slice impl from Str<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::AttrBinding;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers};
use dom::node::Node;
use dom::window::Window;
use dom::virtualmethods::vtable_for;
use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
use std::cell::{Ref, RefCell};
use std::mem;

pub enum AttrSettingType {
    FirstSetAttr,
    ReplacedAttr,
}

#[deriving(PartialEq, Clone, Encodable)]
pub enum AttrValue {
    StringAttrValue(DOMString),
    TokenListAttrValue(DOMString, Vec<(uint, uint)>),
    UIntAttrValue(DOMString, u32),
    AtomAttrValue(Atom),
}

impl AttrValue {
    pub fn from_tokenlist(list: DOMString) -> AttrValue {
        let mut indexes = vec![];
        let mut last_index: uint = 0;
        for (index, ch) in list.as_slice().char_indices() {
            if HTML_SPACE_CHARACTERS.iter().any(|&space| space == ch) {
                indexes.push((last_index, index));
                last_index = index + 1;
            }
        }
        return TokenListAttrValue(list, indexes);
    }

    pub fn from_u32(string: DOMString, default: u32) -> AttrValue {
        let result: u32 = from_str(string.as_slice()).unwrap_or(default);
        UIntAttrValue(string, result)
    }

    pub fn from_atomic(string: DOMString) -> AttrValue {
        let value = Atom::from_slice(string.as_slice());
        AtomAttrValue(value)
    }

}

impl Str for AttrValue {
    fn as_slice<'a>(&'a self) -> &'a str {
        match *self {
            StringAttrValue(ref value) |
            TokenListAttrValue(ref value, _) |
            UIntAttrValue(ref value, _) => value.as_slice(),
            AtomAttrValue(ref value) => value.as_slice(),
        }
    }
}

#[deriving(Encodable)]
pub struct Attr {
    reflector_: Reflector,
    local_name: Atom,
    value: Traceable<RefCell<AttrValue>>,
    pub name: Atom,
    pub namespace: Namespace,
    pub prefix: Option<DOMString>,

    /// the element that owns this attribute.
    owner: JS<Element>,
}

impl Reflectable for Attr {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

impl Attr {
    fn new_inherited(local_name: Atom, value: AttrValue,
                     name: Atom, namespace: Namespace,
                     prefix: Option<DOMString>, owner: &JSRef<Element>) -> Attr {
        Attr {
            reflector_: Reflector::new(),
            local_name: local_name,
            value: Traceable::new(RefCell::new(value)),
            name: name,
            namespace: namespace,
            prefix: prefix,
            owner: JS::from_rooted(owner),
        }
    }

    pub fn new(window: &JSRef<Window>, local_name: Atom, value: AttrValue,
               name: Atom, namespace: Namespace,
               prefix: Option<DOMString>, owner: &JSRef<Element>) -> Temporary<Attr> {
        let attr = Attr::new_inherited(local_name, value, name, namespace, prefix, owner);
        reflect_dom_object(box attr, &Window(*window), AttrBinding::Wrap)
    }

    pub fn set_value(&self, set_type: AttrSettingType, value: AttrValue) {
        let owner = self.owner.root();
        let node: &JSRef<Node> = NodeCast::from_ref(&*owner);
        let namespace_is_null = self.namespace == namespace::Null;

        match set_type {
            ReplacedAttr => {
                if namespace_is_null {
                    vtable_for(node).before_remove_attr(
                        self.local_name(),
                        self.value().as_slice().to_string())
                }
            }
            FirstSetAttr => {}
        }

        *self.value.deref().borrow_mut() = value;

        if namespace_is_null {
            vtable_for(node).after_set_attr(
                self.local_name(),
                self.value().as_slice().to_string())
        }
    }

    pub fn value<'a>(&'a self) -> Ref<'a, AttrValue> {
        self.value.deref().borrow()
    }

    pub fn local_name<'a>(&'a self) -> &'a Atom {
        &self.local_name
    }
}

impl<'a> AttrMethods for JSRef<'a, Attr> {
    fn LocalName(&self) -> DOMString {
        self.local_name().as_slice().to_string()
    }

    fn Value(&self) -> DOMString {
        self.value().as_slice().to_string()
    }

    fn SetValue(&self, value: DOMString) {
        let owner = self.owner.root();
        let value = owner.deref().parse_attribute(
            &self.namespace, self.local_name(), value);
        self.set_value(ReplacedAttr, value);
    }

    fn Name(&self) -> DOMString {
        self.name.as_slice().to_string()
    }

    fn GetNamespaceURI(&self) -> Option<DOMString> {
        match self.namespace.to_str() {
            "" => None,
            url => Some(url.to_string()),
        }
    }

    fn GetPrefix(&self) -> Option<DOMString> {
        self.prefix.clone()
    }
}

pub trait AttrHelpersForLayout {
    unsafe fn value_ref_forever(&self) -> &'static str;
    unsafe fn value_atom_forever(&self) -> Option<Atom>;
}

impl AttrHelpersForLayout for Attr {
    unsafe fn value_ref_forever(&self) -> &'static str {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        value.as_slice()
    }

    unsafe fn value_atom_forever(&self) -> Option<Atom> {
        // cast to point to T in RefCell<T> directly
        let value = mem::transmute::<&RefCell<AttrValue>, &AttrValue>(self.value.deref());
        match *value {
            AtomAttrValue(ref val) => Some(val.clone()),
            _ => None,
        }
    }
}
<|endoftext|>