<commit_before>use sqlite3_raw::*;
use macros;
use std::slice;
use std::os::raw::c_void;
use std::ptr;
use std::mem;

enum VirtualEponymity {
    NonEponymous,
    Eponymous,
    EponymousOnly
}
trait VirtualTable {
    type Cursor;
    fn vtable_eponymity() -> &'static VirtualEponymity;
    fn vtable_definition() -> &'static str;
    fn connect(&mut self);
    fn disconnect(&mut self);
    fn open_cursor(&mut self) -> Self::Cursor;
    fn close_cursor(&mut self, cursor: Self::Cursor);
    fn next(&mut self,      cursor: &Self::Cursor);
    fn column(&mut self,    cursor: &Self::Cursor);
    fn rowid(&self,         cursor: &Self::Cursor);
    fn eof(&self,           cursor: &Self::Cursor);
    fn filter(&self,
        idx_num: i32,
        idx_str: &str,
        args: &[*const i8],
        cursor: &Self::Cursor);
    fn best_index(&self,
        tab: *mut sqlite3_vtab,
        p_idx_info: *mut sqlite3_index_info);
}

unsafe fn infer_sqlite3_malloc<T>() -> Option<*mut T> {
    let size = mem::size_of::<T>();
    let mut p = sql_call!(malloc)(size as i32) as *mut T;
    if p.is_null() { None } else { Some(p) }
}

struct VTableWrapper<T> {
    base: sqlite3_vtab,
    inner: T
}
struct CursorWrapper<T> {
    base: sqlite3_vtab_cursor,
    inner: T
}

#[repr(C)]
struct RangeCursor {
    base: sqlite3_vtab_cursor,
    rowid: i64,
    value: i64,
    start: i64,
    stop: i64,
    step: i64
}
#[repr(C)]
struct RangeVTab {
    base: sqlite3_vtab
    // You can put more here later.
}

unsafe extern "C" fn range_connect(
    db: *mut sqlite3,
    state: *mut c_void,
    argc: i32,
    argv: *const *const i8,
    pp_vtab: *mut *mut sqlite3_vtab,
    pz_err_: *mut *mut i8
) -> i32 {
    or_die!(sql_call!(declare_vtab)(db, const_cstr!(
        "CREATE TABLE range(value, start HIDDEN, stop HIDDEN, step HIDDEN);").as_ptr()));

    // declare vtab succeeded
    assert_ok!(match infer_sqlite3_malloc::<RangeVTab>() {
        None => SQLITE_NOMEM as i32,
        Some(ptab) => {
            // Pass this pointer back to sqlite
            *pp_vtab = ptab as *mut sqlite3_vtab;
            SQLITE_OK
        }
    })
}

unsafe extern "C" fn range_disconnect(vtab: *mut sqlite3_vtab) -> i32 {
  sql_call!(free)(vtab as *mut c_void);
  SQLITE_OK
}

/*
** Constructor for a new RangeCursor object.
*/
unsafe extern "C" fn range_open(p: *mut sqlite3_vtab, pp_cursor: *mut *mut sqlite3_vtab_cursor) -> i32 {
    assert_ok!(match infer_sqlite3_malloc::<RangeCursor>() {
        Some(pcur) => {
            *pp_cursor = pcur as *mut sqlite3_vtab_cursor;
            SQLITE_OK
        },
        None => SQLITE_NOMEM
    })
}

/*
** Destructor for a RangeCursor.
*/
unsafe extern "C" fn range_close(cur: *mut sqlite3_vtab_cursor) -> i32 {
    sql_call!(free)(cur as *mut c_void);
    SQLITE_OK
}

/*
** Advance a RangeCursor to its next row of output.
*/
unsafe extern "C" fn range_next(cur: *mut sqlite3_vtab_cursor) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_mut().unwrap();
    pcur.value += pcur.step;
    pcur.rowid += 1;
    SQLITE_OK
}

/*
** Return values of columns for the row at which the RangeCursor
** is currently pointing.
*/
unsafe extern "C" fn range_column(
  cur: *mut sqlite3_vtab_cursor,   /* The cursor */
  ctx: *mut sqlite3_context,       /* First argument to sqlite3_result_...() */
  i: i32                           /* Which column to return */
) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_ref().unwrap();
    let x = match i {
        SERIES_COLUMN_START =>  pcur.start,
        SERIES_COLUMN_STOP =>   pcur.stop,
        SERIES_COLUMN_STEP =>   pcur.step,
        _ =>                    pcur.value
    };
    sql_call!(result_int64)(ctx, x);
    SQLITE_OK
}

/*
** Return the rowid for the current row. In this implementation, the
** first row returned is assigned rowid value 1, and each subsequent
** row a value 1 more than that of the previous.
*/
unsafe extern "C" fn range_rowid(
    cur: *mut sqlite3_vtab_cursor,
    p_rowid: *mut sqlite_int64) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_ref().unwrap();
    *p_rowid = pcur.rowid;
    SQLITE_OK
}

/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
unsafe extern "C" fn range_eof(cur: *mut sqlite3_vtab_cursor) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_ref().unwrap();
    (if pcur.step < 0 {
        pcur.value < pcur.start
    } else {
        pcur.value > pcur.stop
    }) as i32
}

/*
** This method is called to "rewind" the RangeCursor object back
** to the first row of output.  This method is always called at least
** once prior to any call to range_Column() or range_Rowid() or
** range_Eof().
**
** The query plan selected by range_BestIndex is passed in the idx_num
** parameter.  (idxStr is not used in this implementation.)  idx_num
** is a bitmask showing which constraints are available:
**
**    1:    start=VALUE
**    2:    stop=VALUE
**    4:    step=VALUE
**
** Also, if bit 8 is set, that means that the series should be output
** in descending order rather than in ascending order.
**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
** (so that range_Eof() will return true) if the table is empty.
*/
unsafe extern "C" fn range_filter(
    cur: *mut sqlite3_vtab_cursor,
    idx_num: i32,
    idx_str: *const i8,
    argc: i32,
    pp_argv: *mut *mut sqlite3_value
) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_mut().unwrap();
    let argv = slice::from_raw_parts(pp_argv, argc as usize);
    let mut i = 0usize;
    if idx_num & 1 != 0 {
        pcur.start = sql_call!(value_int64)(argv[i]);
        i += 1;
    } else {
        pcur.start = 0;
    }
    if idx_num & 2 != 0{
        pcur.stop = sql_call!(value_int64)(argv[i]);
        i += 1;
    } else {
        pcur.stop = 0xffffffff;
    }
    if idx_num & 4 != 0 {
        pcur.step = sql_call!(value_int64)(argv[i]);
        i += 1;
        if pcur.step < 1 {
            pcur.step = 1;
        }
    } else {
        pcur.step = 1;
    }
    if idx_num & 8 != 0 {
        //pcur->isDesc = 1;
        pcur.value = pcur.stop;
        if pcur.step > 0 {
            pcur.value -= (pcur.stop - pcur.start) % pcur.step;
        }
    } else {
        //pcur->isDesc = 0;
        pcur.value = pcur.start;
    }
    pcur.rowid = 1;
    SQLITE_OK
}

/*
** SQLite will invoke this method one or more times while planning a query
** that uses the generate_series virtual table.  This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** In this implementation idx_num is used to represent the
** query plan.  idxStr is unused.
**
** The query plan is represented by bits in idx_num:
**
**  (1)  start = $value  -- constraint exists
**  (2)  stop = $value   -- constraint exists
**  (4)  step = $value   -- constraint exists
**  (8)  output in descending order
*/
unsafe extern "C" fn range_best_index(
  tab: *mut sqlite3_vtab,
  p_idx_info: *mut sqlite3_index_info
) -> i32 {
    let mut idx_num = 0i32;        /* The query plan bitmask */
    let mut start_idx = -1i32;     /* Index of the start= constraint, or -1 if none */
    let mut stop_idx = -1i32;      /* Index of the stop= constraint, or -1 if none */
    let mut step_idx = -1i32;      /* Index of the step= constraint, or -1 if none */
    let mut n_arg = 0i32;          /* Number of arguments that range_Filter() expects */
    let idx_info = p_idx_info.as_mut().unwrap();
    let constraints = slice::from_raw_parts((*p_idx_info).aConstraint, (*p_idx_info).nConstraint as usize);
    for (ui, constraint) in constraints.iter().enumerate() {
        let i = ui as i32;
        if constraint.usable != 0 && constraint.op == SQLITE_INDEX_CONSTRAINT_EQ {
            match constraint.iColumn {
                SERIES_COLUMN_START => {
                    start_idx = i;
                    idx_num |= 1;
                },
                SERIES_COLUMN_STOP => {
                    stop_idx = i;
                    idx_num |= 2;
                },
                SERIES_COLUMN_STEP => {
                    step_idx = i;
                    idx_num |= 4;
                },
                _ => ()
            }
        }
    }

    if start_idx >= 0 {
        n_arg += 1;
        idx_info.aConstraintUsage.offset(start_idx as isize).as_mut().unwrap().argvIndex = n_arg;
        idx_info.aConstraintUsage.offset(start_idx as isize).as_mut().unwrap().omit = 1; // No longer checked by sqlite
    }
    if stop_idx >= 0 {
        n_arg += 1;
        idx_info.aConstraintUsage.offset(stop_idx as isize).as_mut().unwrap().argvIndex = n_arg;
        idx_info.aConstraintUsage.offset(stop_idx as isize).as_mut().unwrap().omit = 1;
    }
    if step_idx >= 0 {
        n_arg += 1;
        idx_info.aConstraintUsage.offset(step_idx as isize).as_mut().unwrap().argvIndex = n_arg;
        idx_info.aConstraintUsage.offset(step_idx as isize).as_mut().unwrap().omit = 1;
    }
    if (idx_num & 3) == 3 {
        /* Both start= and stop= boundaries are available.  This is the
        ** the preferred case */
        idx_info.estimatedCost = (2 - ((idx_num & 4)!=0) as i64) as f64;
        idx_info.estimatedRows = 1000;
        if idx_info.nOrderBy == 1 {
            if idx_info.aOrderBy.as_ref().unwrap().desc != 0 {
                idx_num |= 8;
            }
            idx_info.orderByConsumed = 1;
        }
    } else {
        /* If either boundary is missing, we have to generate a huge span
        ** of numbers.  Make this case very expensive so that the query
        ** planner will work hard to avoid it. */
        idx_info.estimatedCost = 2147483647.0f64;
        idx_info.estimatedRows = 2147483647;
    }
    idx_info.idxNum = idx_num;
    SQLITE_OK
}

/*
** This following structure defines all the methods for the
** generate_series virtual table.
*/
pub static range_module : sqlite3_module = sqlite3_module {
    iVersion:       0,
    xCreate:        None,
    xConnect:       Some(range_connect),
    xBestIndex:     Some(range_best_index),
    xDisconnect:    Some(range_disconnect),
    xDestroy:       None,
    xOpen:          Some(range_open),   // open a cursor
    xClose:         Some(range_close),  // close a cursor
    xFilter:        Some(range_filter), // configure scan constraints
    xNext:          Some(range_next),   // advance a cursor
    xEof:           Some(range_eof),    // check for end of scan
    xColumn:        Some(range_column), // read data
    xRowid:         Some(range_rowid),  // read data
    xUpdate:        None,
    xBegin:         None,
    xSync:          None,
    xCommit:        None,
    xRollback:      None,
    xFindFunction:  None,
    xRename:        None,
    // The following are for version 2 and above
    xSavepoint:     None,
    xRelease:       None,
    xRollbackTo:    None
};

const SERIES_COLUMN_VALUE : i32 = 0;
const SERIES_COLUMN_START : i32 = 1;
const SERIES_COLUMN_STOP  : i32 = 2;
const SERIES_COLUMN_STEP  : i32 = 3;
<commit_msg>Started a rust implementation<commit_after>use sqlite3_raw::*;
use macros;
use std::slice;
use std::os::raw::c_void;
use std::ptr;
use std::mem;

enum VirtualEponymity {
    NonEponymous,
    Eponymous,
    EponymousOnly
}
trait VirtualTable {
    type Cursor;
    fn vtable_eponymity() -> &'static VirtualEponymity;
    fn vtable_definition() -> &'static str;
    fn connect(&mut self);
    fn disconnect(&mut self);
    fn open_cursor(&mut self) -> Self::Cursor;
    fn close_cursor(&mut self, cursor: Self::Cursor);
    fn next(&mut self,      cursor: &Self::Cursor);
    fn column(&mut self,    cursor: &Self::Cursor);
    fn rowid(&self,         cursor: &Self::Cursor);
    fn eof(&self,           cursor: &Self::Cursor);
    fn filter(&self,
        idx_num: i32,
        idx_str: &str,
        args: &[*const i8],
        cursor: &Self::Cursor);
    fn best_index(&self,
        tab: *mut sqlite3_vtab,
        p_idx_info: *mut sqlite3_index_info);
}

unsafe fn infer_sqlite3_malloc<T>() -> Option<*mut T> {
    let size = mem::size_of::<T>();
    let mut p = sql_call!(malloc)(size as i32) as *mut T;
    if p.is_null() { None } else { Some(p) }
}

struct VTableWrapper<T> {
    base: sqlite3_vtab,
    inner: T
}
struct CursorWrapper<T> {
    base: sqlite3_vtab_cursor,
    inner: T
}

struct RangeCursor {
    rowid: i64,
    value: i64,
    start: i64,
    stop: i64,
    step: i64
}
struct RangeVTab ()

unsafe extern "C" fn range_connect(
    db: *mut sqlite3,
    state: *mut c_void,
    argc: i32,
    argv: *const *const i8,
    pp_vtab: *mut *mut sqlite3_vtab,
    pz_err_: *mut *mut i8
) -> i32 {
    or_die!(sql_call!(declare_vtab)(db, const_cstr!(
        "CREATE TABLE range(value, start HIDDEN, stop HIDDEN, step HIDDEN);").as_ptr()));

    // declare vtab succeeded
    assert_ok!(match infer_sqlite3_malloc::<VTableWrapper<RangeVTab>>() {
        None => SQLITE_NOMEM as i32,
        Some(ptab) => {
            // Pass this pointer back to sqlite
            *pp_vtab = ptab as *mut sqlite3_vtab;
            SQLITE_OK
        }
    })
}

unsafe extern "C" fn range_disconnect(vtab: *mut sqlite3_vtab) -> i32 {
  sql_call!(free)(vtab as *mut c_void);
  SQLITE_OK
}

/*
** Constructor for a new RangeCursor object.
*/
unsafe extern "C" fn range_open(p: *mut sqlite3_vtab, pp_cursor: *mut *mut sqlite3_vtab_cursor) -> i32 {
    assert_ok!(match infer_sqlite3_malloc::<CursorWrapper<RangeCursor>>() {
        Some(pcur) => {
            *pp_cursor = pcur as *mut sqlite3_vtab_cursor;
            SQLITE_OK
        },
        None => SQLITE_NOMEM
    })
}

/*
** Destructor for a RangeCursor.
*/
unsafe extern "C" fn range_close(cur: *mut sqlite3_vtab_cursor) -> i32 {
    sql_call!(free)(cur as *mut c_void);
    SQLITE_OK
}

/*
** Advance a RangeCursor to its next row of output.
*/
unsafe extern "C" fn range_next(cur: *mut sqlite3_vtab_cursor) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_mut().unwrap();
    pcur.value += pcur.step;
    pcur.rowid += 1;
    SQLITE_OK
}

/*
** Return values of columns for the row at which the RangeCursor
** is currently pointing.
*/
unsafe extern "C" fn range_column(
  cur: *mut sqlite3_vtab_cursor,   /* The cursor */
  ctx: *mut sqlite3_context,       /* First argument to sqlite3_result_...() */
  i: i32                           /* Which column to return */
) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_ref().unwrap();
    let x = match i {
        SERIES_COLUMN_START =>  pcur.start,
        SERIES_COLUMN_STOP =>   pcur.stop,
        SERIES_COLUMN_STEP =>   pcur.step,
        _ =>                    pcur.value
    };
    sql_call!(result_int64)(ctx, x);
    SQLITE_OK
}

/*
** Return the rowid for the current row. In this implementation, the
** first row returned is assigned rowid value 1, and each subsequent
** row a value 1 more than that of the previous.
*/
unsafe extern "C" fn range_rowid(
    cur: *mut sqlite3_vtab_cursor,
    p_rowid: *mut sqlite_int64) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_ref().unwrap();
    *p_rowid = pcur.rowid;
    SQLITE_OK
}

/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
unsafe extern "C" fn range_eof(cur: *mut sqlite3_vtab_cursor) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_ref().unwrap();
    (if pcur.step < 0 {
        pcur.value < pcur.start
    } else {
        pcur.value > pcur.stop
    }) as i32
}

/*
** This method is called to "rewind" the RangeCursor object back
** to the first row of output.  This method is always called at least
** once prior to any call to range_Column() or range_Rowid() or
** range_Eof().
**
** The query plan selected by range_BestIndex is passed in the idx_num
** parameter.  (idxStr is not used in this implementation.)  idx_num
** is a bitmask showing which constraints are available:
**
**    1:    start=VALUE
**    2:    stop=VALUE
**    4:    step=VALUE
**
** Also, if bit 8 is set, that means that the series should be output
** in descending order rather than in ascending order.
**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
** (so that range_Eof() will return true) if the table is empty.
*/
unsafe extern "C" fn range_filter(
    cur: *mut sqlite3_vtab_cursor,
    idx_num: i32,
    idx_str: *const i8,
    argc: i32,
    pp_argv: *mut *mut sqlite3_value
) -> i32 {
    let pcur = (cur as *mut RangeCursor).as_mut().unwrap();
    let argv = slice::from_raw_parts(pp_argv, argc as usize);
    let mut i = 0usize;
    if idx_num & 1 != 0 {
        pcur.start = sql_call!(value_int64)(argv[i]);
        i += 1;
    } else {
        pcur.start = 0;
    }
    if idx_num & 2 != 0{
        pcur.stop = sql_call!(value_int64)(argv[i]);
        i += 1;
    } else {
        pcur.stop = 0xffffffff;
    }
    if idx_num & 4 != 0 {
        pcur.step = sql_call!(value_int64)(argv[i]);
        i += 1;
        if pcur.step < 1 {
            pcur.step = 1;
        }
    } else {
        pcur.step = 1;
    }
    if idx_num & 8 != 0 {
        //pcur->isDesc = 1;
        pcur.value = pcur.stop;
        if pcur.step > 0 {
            pcur.value -= (pcur.stop - pcur.start) % pcur.step;
        }
    } else {
        //pcur->isDesc = 0;
        pcur.value = pcur.start;
    }
    pcur.rowid = 1;
    SQLITE_OK
}

/*
** SQLite will invoke this method one or more times while planning a query
** that uses the generate_series virtual table.  This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** In this implementation idx_num is used to represent the
** query plan.  idxStr is unused.
**
** The query plan is represented by bits in idx_num:
**
**  (1)  start = $value  -- constraint exists
**  (2)  stop = $value   -- constraint exists
**  (4)  step = $value   -- constraint exists
**  (8)  output in descending order
*/
unsafe extern "C" fn range_best_index(
  tab: *mut sqlite3_vtab,
  p_idx_info: *mut sqlite3_index_info
) -> i32 {
    let mut idx_num = 0i32;        /* The query plan bitmask */
    let mut start_idx = -1i32;     /* Index of the start= constraint, or -1 if none */
    let mut stop_idx = -1i32;      /* Index of the stop= constraint, or -1 if none */
    let mut step_idx = -1i32;      /* Index of the step= constraint, or -1 if none */
    let mut n_arg = 0i32;          /* Number of arguments that range_Filter() expects */
    let idx_info = p_idx_info.as_mut().unwrap();
    let constraints = slice::from_raw_parts((*p_idx_info).aConstraint, (*p_idx_info).nConstraint as usize);
    for (ui, constraint) in constraints.iter().enumerate() {
        let i = ui as i32;
        if constraint.usable != 0 && constraint.op == SQLITE_INDEX_CONSTRAINT_EQ {
            match constraint.iColumn {
                SERIES_COLUMN_START => {
                    start_idx = i;
                    idx_num |= 1;
                },
                SERIES_COLUMN_STOP => {
                    stop_idx = i;
                    idx_num |= 2;
                },
                SERIES_COLUMN_STEP => {
                    step_idx = i;
                    idx_num |= 4;
                },
                _ => ()
            }
        }
    }

    if start_idx >= 0 {
        n_arg += 1;
        idx_info.aConstraintUsage.offset(start_idx as isize).as_mut().unwrap().argvIndex = n_arg;
        idx_info.aConstraintUsage.offset(start_idx as isize).as_mut().unwrap().omit = 1; // No longer checked by sqlite
    }
    if stop_idx >= 0 {
        n_arg += 1;
        idx_info.aConstraintUsage.offset(stop_idx as isize).as_mut().unwrap().argvIndex = n_arg;
        idx_info.aConstraintUsage.offset(stop_idx as isize).as_mut().unwrap().omit = 1;
    }
    if step_idx >= 0 {
        n_arg += 1;
        idx_info.aConstraintUsage.offset(step_idx as isize).as_mut().unwrap().argvIndex = n_arg;
        idx_info.aConstraintUsage.offset(step_idx as isize).as_mut().unwrap().omit = 1;
    }
    if (idx_num & 3) == 3 {
        /* Both start= and stop= boundaries are available.  This is the
        ** the preferred case */
        idx_info.estimatedCost = (2 - ((idx_num & 4)!=0) as i64) as f64;
        idx_info.estimatedRows = 1000;
        if idx_info.nOrderBy == 1 {
            if idx_info.aOrderBy.as_ref().unwrap().desc != 0 {
                idx_num |= 8;
            }
            idx_info.orderByConsumed = 1;
        }
    } else {
        /* If either boundary is missing, we have to generate a huge span
        ** of numbers.  Make this case very expensive so that the query
        ** planner will work hard to avoid it. */
        idx_info.estimatedCost = 2147483647.0f64;
        idx_info.estimatedRows = 2147483647;
    }
    idx_info.idxNum = idx_num;
    SQLITE_OK
}

/*
** This following structure defines all the methods for the
** generate_series virtual table.
*/
pub static range_module : sqlite3_module = sqlite3_module {
    iVersion:       0,
    xCreate:        None,
    xConnect:       Some(range_connect),
    xBestIndex:     Some(range_best_index),
    xDisconnect:    Some(range_disconnect),
    xDestroy:       None,
    xOpen:          Some(range_open),   // open a cursor
    xClose:         Some(range_close),  // close a cursor
    xFilter:        Some(range_filter), // configure scan constraints
    xNext:          Some(range_next),   // advance a cursor
    xEof:           Some(range_eof),    // check for end of scan
    xColumn:        Some(range_column), // read data
    xRowid:         Some(range_rowid),  // read data
    xUpdate:        None,
    xBegin:         None,
    xSync:          None,
    xCommit:        None,
    xRollback:      None,
    xFindFunction:  None,
    xRename:        None,
    // The following are for version 2 and above
    xSavepoint:     None,
    xRelease:       None,
    xRollbackTo:    None
};

const SERIES_COLUMN_VALUE : i32 = 0;
const SERIES_COLUMN_START : i32 = 1;
const SERIES_COLUMN_STOP  : i32 = 2;
const SERIES_COLUMN_STEP  : i32 = 3;
<|endoftext|>


<commit_before>use nix::Error;
use nix::errno::*;
use nix::fcntl::{openat, open, OFlag, readlink, readlinkat, renameat};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use tempfile::{self, NamedTempFile};
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let fd = openat(dirfd,
                    tmp.path().file_name().unwrap(),
                    OFlag::O_RDONLY,
                    Mode::empty()).unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
               Error::Sys(Errno::ENOENT));
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);

}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::io::prelude::*;
    use std::io::SeekFrom;
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
            wr, None, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_env = "uclibc",
          target_env = "freebsd"))]
mod test_posix_fadvise {

    use tempfile::NamedTempFile;
    use std::os::unix::io::{RawFd, AsRawFd};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
                                 .unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_os = "freebsd"))]
mod test_posix_fallocate {

    use tempfile::NamedTempFile;
    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL)
                | Sys(Errno::ENODEV)
                | Sys(Errno::ESPIPE)
                | Sys(Errno::EBADF) => (),
            errno =>
                panic!(
                    "unexpected errno {}",
                    errno,
                ),
        }
    }
}
<commit_msg>Add fcntl tests for OFD locks<commit_after>use nix::Error;
use nix::errno::*;
use nix::fcntl::{openat, open, OFlag, readlink, readlinkat, renameat};
use nix::sys::stat::Mode;
use nix::unistd::{close, read};
use tempfile::{self, NamedTempFile};
use std::fs::File;
use std::io::prelude::*;
use std::os::unix::fs;

#[test]
fn test_openat() {
    const CONTENTS: &[u8] = b"abcd";
    let mut tmp = NamedTempFile::new().unwrap();
    tmp.write_all(CONTENTS).unwrap();

    let dirfd = open(tmp.path().parent().unwrap(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let fd = openat(dirfd,
                    tmp.path().file_name().unwrap(),
                    OFlag::O_RDONLY,
                    Mode::empty()).unwrap();

    let mut buf = [0u8; 1024];
    assert_eq!(4, read(fd, &mut buf).unwrap());
    assert_eq!(CONTENTS, &buf[0..4]);

    close(fd).unwrap();
    close(dirfd).unwrap();
}

#[test]
fn test_renameat() {
    let old_dir = tempfile::tempdir().unwrap();
    let old_dirfd = open(old_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    let old_path = old_dir.path().join("old");
    File::create(&old_path).unwrap();
    let new_dir = tempfile::tempdir().unwrap();
    let new_dirfd = open(new_dir.path(), OFlag::empty(), Mode::empty()).unwrap();
    renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap();
    assert_eq!(renameat(Some(old_dirfd), "old", Some(new_dirfd), "new").unwrap_err(),
               Error::Sys(Errno::ENOENT));
    close(old_dirfd).unwrap();
    close(new_dirfd).unwrap();
    assert!(new_dir.path().join("new").exists());
}

#[test]
fn test_readlink() {
    let tempdir = tempfile::tempdir().unwrap();
    let src = tempdir.path().join("a");
    let dst = tempdir.path().join("b");
    println!("a: {:?}, b: {:?}", &src, &dst);
    fs::symlink(&src.as_path(), &dst.as_path()).unwrap();
    let dirfd = open(tempdir.path(),
                     OFlag::empty(),
                     Mode::empty()).unwrap();
    let expected_dir = src.to_str().unwrap();

    assert_eq!(readlink(&dst).unwrap().to_str().unwrap(), expected_dir);
    assert_eq!(readlinkat(dirfd, "b").unwrap().to_str().unwrap(), expected_dir);

}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::{BufRead, BufReader, SeekFrom};
    use std::os::unix::prelude::*;

    use libc::loff_t;

    use nix::fcntl::*;
    use nix::sys::stat::fstat;
    use nix::sys::uio::IoVec;
    use nix::unistd::{close, pipe, read, write};

    use tempfile::{tempfile, NamedTempFile};

    /// This test creates a temporary file containing the contents
    /// 'foobarbaz' and uses the `copy_file_range` call to transfer
    /// 3 bytes at offset 3 (`bar`) to another empty file at offset 0. The
    /// resulting file is read and should contain the contents `bar`.
    /// The from_offset should be updated by the call to reflect
    /// the 3 bytes read (6).
    ///
    /// FIXME: This test is disabled for linux based builds, because Travis
    /// Linux version is too old for `copy_file_range`.
    #[test]
    #[ignore]
    fn test_copy_file_range() {
        const CONTENTS: &[u8] = b"foobarbaz";

        let mut tmp1 = tempfile().unwrap();
        let mut tmp2 = tempfile().unwrap();

        tmp1.write_all(CONTENTS).unwrap();
        tmp1.flush().unwrap();

        let mut from_offset: i64 = 3;
        copy_file_range(
            tmp1.as_raw_fd(),
            Some(&mut from_offset),
            tmp2.as_raw_fd(),
            None,
            3,
        )
        .unwrap();

        let mut res: String = String::new();
        tmp2.seek(SeekFrom::Start(0)).unwrap();
        tmp2.read_to_string(&mut res).unwrap();

        assert_eq!(res, String::from("bar"));
        assert_eq!(from_offset, 6);
    }

    #[test]
    fn test_splice() {
        const CONTENTS: &[u8] = b"abcdef123456";
        let mut tmp = tempfile().unwrap();
        tmp.write_all(CONTENTS).unwrap();

        let (rd, wr) = pipe().unwrap();
        let mut offset: loff_t = 5;
        let res = splice(tmp.as_raw_fd(), Some(&mut offset),
            wr, None, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];
        assert_eq!(2, read(rd, &mut buf).unwrap());
        assert_eq!(b"f1", &buf[0..2]);
        assert_eq!(7, offset);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_tee() {
        let (rd1, wr1) = pipe().unwrap();
        let (rd2, wr2) = pipe().unwrap();

        write(wr1, b"abc").unwrap();
        let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

        assert_eq!(2, res);

        let mut buf = [0u8; 1024];

        // Check the tee'd bytes are at rd2.
        assert_eq!(2, read(rd2, &mut buf).unwrap());
        assert_eq!(b"ab", &buf[0..2]);

        // Check all the bytes are still at rd1.
        assert_eq!(3, read(rd1, &mut buf).unwrap());
        assert_eq!(b"abc", &buf[0..3]);

        close(rd1).unwrap();
        close(wr1).unwrap();
        close(rd2).unwrap();
        close(wr2).unwrap();
    }

    #[test]
    fn test_vmsplice() {
        let (rd, wr) = pipe().unwrap();

        let buf1 = b"abcdef";
        let buf2 = b"defghi";
        let mut iovecs = Vec::with_capacity(2);
        iovecs.push(IoVec::from_slice(&buf1[0..3]));
        iovecs.push(IoVec::from_slice(&buf2[0..3]));

        let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

        assert_eq!(6, res);

        // Check the bytes can be read at rd.
        let mut buf = [0u8; 32];
        assert_eq!(6, read(rd, &mut buf).unwrap());
        assert_eq!(b"abcdef", &buf[0..6]);

        close(rd).unwrap();
        close(wr).unwrap();
    }

    #[test]
    fn test_fallocate() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        fallocate(fd, FallocateFlags::empty(), 0, 100).unwrap();

        // Check if we read exactly 100 bytes
        let mut buf = [0u8; 200];
        assert_eq!(100, read(fd, &mut buf).unwrap());
    }

    #[test]
    fn test_ofd_locks() {
        let tmp = NamedTempFile::new().unwrap();

        let fd = tmp.as_raw_fd();
        let inode = fstat(fd).unwrap().st_ino;

        let mut flock = libc::flock {
            l_type: libc::F_WRLCK as libc::c_short,
            l_whence: libc::SEEK_SET as libc::c_short,
            l_start: 0,
            l_len: 0,
            l_pid: 0,
        };
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "WRITE".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));

        flock.l_type = libc::F_RDLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(Some(("OFDLCK".to_string(), "READ".to_string())), lock_info(inode));

        flock.l_type = libc::F_UNLCK as libc::c_short;
        fcntl(fd, FcntlArg::F_OFD_SETLKW(&flock)).unwrap();
        assert_eq!(None, lock_info(inode));
    }

    fn lock_info(inode: u64) -> Option<(String, String)> {
        let file = File::open("/proc/locks").unwrap();
        let buf = BufReader::new(file);

        for line in buf.lines() {
            let line = line.unwrap();
            let parts: Vec<_> = line.split_whitespace().collect();
            let lock_type = parts[1];
            let lock_access = parts[3];
            let ino_parts: Vec<_> = parts[5].split(':').collect();
            let ino: u64 = ino_parts[2].parse().unwrap();
            if ino == inode {
                return Some((lock_type.to_string(), lock_access.to_string()))
            }
        }
        None
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_env = "uclibc",
          target_env = "freebsd"))]
mod test_posix_fadvise {

    use tempfile::NamedTempFile;
    use std::os::unix::io::{RawFd, AsRawFd};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn test_success() {
        let tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fadvise(fd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED).unwrap();

        assert_eq!(res, 0);
    }

    #[test]
    fn test_errno() {
        let (rd, _wr) = pipe().unwrap();
        let errno = posix_fadvise(rd as RawFd, 0, 100, PosixFadviseAdvice::POSIX_FADV_WILLNEED)
                                 .unwrap();
        assert_eq!(errno, Errno::ESPIPE as i32);
    }
}

#[cfg(any(target_os = "linux",
          target_os = "android",
          target_os = "emscripten",
          target_os = "fuchsia",
          any(target_os = "wasi", target_env = "wasi"),
          target_os = "freebsd"))]
mod test_posix_fallocate {

    use tempfile::NamedTempFile;
    use std::{io::Read, os::unix::io::{RawFd, AsRawFd}};
    use nix::errno::Errno;
    use nix::fcntl::*;
    use nix::unistd::pipe;

    #[test]
    fn success() {
        const LEN: usize = 100;
        let mut tmp = NamedTempFile::new().unwrap();
        let fd = tmp.as_raw_fd();
        let res = posix_fallocate(fd, 0, LEN as libc::off_t);
        match res {
            Ok(_) => {
                let mut data = [1u8; LEN];
                assert_eq!(tmp.read(&mut data).expect("read failure"), LEN);
                assert_eq!(&data[..], &[0u8; LEN][..]);
            }
            Err(nix::Error::Sys(Errno::EINVAL)) => {
                // POSIX requires posix_fallocate to return EINVAL both for
                // invalid arguments (i.e. len < 0) and if the operation is not
                // supported by the file system.
                // There's no way to tell for sure whether the file system
                // supports posix_fallocate, so we must pass the test if it
                // returns EINVAL.
            }
            _ => res.unwrap(),
        }
    }

    #[test]
    fn errno() {
        let (rd, _wr) = pipe().unwrap();
        let err = posix_fallocate(rd as RawFd, 0, 100).unwrap_err();
        use nix::Error::Sys;
        match err {
            Sys(Errno::EINVAL)
                | Sys(Errno::ENODEV)
                | Sys(Errno::ESPIPE)
                | Sys(Errno::EBADF) => (),
            errno =>
                panic!(
                    "unexpected errno {}",
                    errno,
                ),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::{io, os};
use std::default::Default;

use test::{black_box, Bencher, TestDesc, TestDescAndFn};
use test::{DynTestName, DynBenchFn, TDynBenchFn};

use html5ever::tokenizer::{TokenSink, Token, Tokenizer, TokenizerOpts};

struct Sink;

impl TokenSink for Sink {
    fn process_token(&mut self, token: Token) {
        // Don't use the token, but make sure we don't get
        // optimized out entirely.
        black_box(token);
    }
}

// This could almost be the TokenSink too, but it's not
// mut within run().
struct Bench {
    input: String,
    clone_only: bool,
    opts: TokenizerOpts,
}

impl Bench {
    fn new(name: &str, size: Option<uint>, clone_only: bool,
           opts: TokenizerOpts) -> Bench {
        let mut path = os::self_exe_path().expect("can't get exe path");
        path.push("../data/bench/");
        path.push(name);
        let mut file = io::File::open(&path).ok().expect("can't open file");
        let file_input = file.read_to_str().ok().expect("can't read file");

        let input = match size {
            None => file_input,
            Some(size) => {
                // Replicate the input in memory up to the desired size.
                let mut input = String::with_capacity(size);
                while input.len() < size {
                    input.push_str(file_input.as_slice());
                }
                input
            }
        };

        Bench {
            input: input,
            clone_only: clone_only,
            opts: opts,
        }
    }
}

impl TDynBenchFn for Bench {
    fn run(&self, bh: &mut Bencher) {
        bh.iter(|| {
            let input = self.input.clone();
            if self.clone_only {
                // Because the tokenizer consumes its buffers, we need
                // to clone inside iter().  We can benchmark this
                // separately and subtract it out.
                black_box(input);
            } else {
                let mut sink = Sink;
                let mut tok = Tokenizer::new(&mut sink, self.opts.clone());
                tok.feed(input);
                tok.end();
            }
        });
    }
}

fn make_bench(name: &str, size: Option<uint>, clone_only: bool,
              opts: TokenizerOpts) -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName([
                "tokenize ".to_string(),
                name.to_string(),
                size.map_or("".to_string(), |s| format!(" size {:7u}", s)),
                (if clone_only { " (clone only)" } else { "" }).to_string(),
                (if opts.exact_errors { " (exact errors)" } else { "" }).to_string(),
            ].concat().to_string()),
            ignore: false,
            should_fail: false,
        },
        testfn: DynBenchFn(box Bench::new(name, size, clone_only, opts)),
    }
}

pub fn tests() -> Vec<TestDescAndFn> {
    let mut tests = vec!(make_bench("lipsum.html", Some(1024*1024), true, Default::default()));

    let mut opts_vec = vec!(Default::default());
    if os::getenv("BENCH_EXACT_ERRORS").is_some() {
        opts_vec.push(TokenizerOpts {
            exact_errors: true,
            .. Default::default()
        });
    }

    for opts in opts_vec.iter() {
        for &file in ["lipsum.html", "lipsum-zh.html", "strong.html"].iter() {
            for &sz in [1024, 1024*1024].iter() {
                tests.push(make_bench(file, Some(sz), false, opts.clone()));
            }
        }

        for &file in ["tiny-fragment.html", "small-fragment.html", "medium-fragment.html"].iter() {
            tests.push(make_bench(file, None, false, opts.clone()));
        }

        if os::getenv("BENCH_UNCOMMITTED").is_some() {
            // Not checked into the repo, so don't include by default.
            for &file in ["webapps.html", "sina.com.cn.html", "wikipedia.html"].iter() {
                let name = "uncommitted/".to_string().append(file);
                tests.push(make_bench(name.as_slice(), None, false, opts.clone()));
            }
        }
    }

    tests
}
<commit_msg>Don't use the webapp spec as a tokenizer benchmark<commit_after>// Copyright 2014 The html5ever Project Developers. See the
// COPYRIGHT file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::{io, os};
use std::default::Default;

use test::{black_box, Bencher, TestDesc, TestDescAndFn};
use test::{DynTestName, DynBenchFn, TDynBenchFn};

use html5ever::tokenizer::{TokenSink, Token, Tokenizer, TokenizerOpts};

struct Sink;

impl TokenSink for Sink {
    fn process_token(&mut self, token: Token) {
        // Don't use the token, but make sure we don't get
        // optimized out entirely.
        black_box(token);
    }
}

// This could almost be the TokenSink too, but it's not
// mut within run().
struct Bench {
    input: String,
    clone_only: bool,
    opts: TokenizerOpts,
}

impl Bench {
    fn new(name: &str, size: Option<uint>, clone_only: bool,
           opts: TokenizerOpts) -> Bench {
        let mut path = os::self_exe_path().expect("can't get exe path");
        path.push("../data/bench/");
        path.push(name);
        let mut file = io::File::open(&path).ok().expect("can't open file");
        let file_input = file.read_to_str().ok().expect("can't read file");

        let input = match size {
            None => file_input,
            Some(size) => {
                // Replicate the input in memory up to the desired size.
                let mut input = String::with_capacity(size);
                while input.len() < size {
                    input.push_str(file_input.as_slice());
                }
                input
            }
        };

        Bench {
            input: input,
            clone_only: clone_only,
            opts: opts,
        }
    }
}

impl TDynBenchFn for Bench {
    fn run(&self, bh: &mut Bencher) {
        bh.iter(|| {
            let input = self.input.clone();
            if self.clone_only {
                // Because the tokenizer consumes its buffers, we need
                // to clone inside iter().  We can benchmark this
                // separately and subtract it out.
                black_box(input);
            } else {
                let mut sink = Sink;
                let mut tok = Tokenizer::new(&mut sink, self.opts.clone());
                tok.feed(input);
                tok.end();
            }
        });
    }
}

fn make_bench(name: &str, size: Option<uint>, clone_only: bool,
              opts: TokenizerOpts) -> TestDescAndFn {
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName([
                "tokenize ".to_string(),
                name.to_string(),
                size.map_or("".to_string(), |s| format!(" size {:7u}", s)),
                (if clone_only { " (clone only)" } else { "" }).to_string(),
                (if opts.exact_errors { " (exact errors)" } else { "" }).to_string(),
            ].concat().to_string()),
            ignore: false,
            should_fail: false,
        },
        testfn: DynBenchFn(box Bench::new(name, size, clone_only, opts)),
    }
}

pub fn tests() -> Vec<TestDescAndFn> {
    let mut tests = vec!(make_bench("lipsum.html", Some(1024*1024), true, Default::default()));

    let mut opts_vec = vec!(Default::default());
    if os::getenv("BENCH_EXACT_ERRORS").is_some() {
        opts_vec.push(TokenizerOpts {
            exact_errors: true,
            .. Default::default()
        });
    }

    for opts in opts_vec.iter() {
        for &file in ["lipsum.html", "lipsum-zh.html", "strong.html"].iter() {
            for &sz in [1024, 1024*1024].iter() {
                tests.push(make_bench(file, Some(sz), false, opts.clone()));
            }
        }

        for &file in ["tiny-fragment.html", "small-fragment.html", "medium-fragment.html"].iter() {
            tests.push(make_bench(file, None, false, opts.clone()));
        }

        if os::getenv("BENCH_UNCOMMITTED").is_some() {
            // Not checked into the repo, so don't include by default.
            for &file in ["sina.com.cn.html", "wikipedia.html"].iter() {
                let name = "uncommitted/".to_string().append(file);
                tests.push(make_bench(name.as_slice(), None, false, opts.clone()));
            }
        }
    }

    tests
}
<|endoftext|>


<commit_before>#[doc = "Code to lex and tokenize css files."]

import comm::{port, chan};
import dom::style;
import option::is_none;
import str::from_bytes;
import vec::push;

import lexer_util::*;

enum ParserState {
    CssElement,
    CssRelation,
    CssDescription,
    CssAttribute
}

type CssLexer = {
    input_state: InputState,
    mut parser_state: ParserState
};

enum Token {
    StartDescription,
    EndDescription,
    Descendant,
    Child,
    Sibling,
    Comma,
    Element(~str),
    Attr(style::Attr),
    Description(~str, ~str),
    Eof
}

trait css_methods {
    fn parse_css() -> Token;
    fn parse_css_relation(c : u8) -> Token;
    fn parse_css_element(c : u8) -> Token;
    fn parse_css_attribute(c : u8) -> Token;
    fn parse_css_description(c: u8) -> Token;
}

impl css_methods of css_methods for CssLexer {
    fn parse_css() -> Token {
        let mut ch: u8;
        alt self.input_state.get() {
            CoeChar(c) { ch = c; }
            CoeEof { ret Eof; }
        }

        let token = alt self.parser_state {
          CssDescription { self.parse_css_description(ch) }
          CssAttribute   { self.parse_css_attribute(ch) }
          CssElement     { self.parse_css_element(ch) }
          CssRelation    { self.parse_css_relation(ch) }
        };

        #debug["token=%?", token];
        ret token;
    }

    fn parse_css_relation(c : u8) -> Token {
        self.parser_state = CssElement;

        let token = alt c {
          '{' as u8  { self.parser_state = CssDescription; StartDescription }
          '>' as u8  { Child }
          '+' as u8  { Sibling }
          ',' as u8  { Comma }
          _          { self.input_state.unget(c); Descendant }
        };

        self.input_state.eat_whitespace();

        ret token;
    }

    fn parse_css_element(c : u8) -> Token {
        assert is_none(self.input_state.lookahead);

        /* Check for special attributes with an implied element,
        or a wildcard which is not a alphabet character.*/
        if c == '.' as u8 || c == '#' as u8 {
            self.parser_state = CssAttribute;
            self.input_state.unget(c);
            ret Element(~"*");
        } else if c == '*' as u8 {
            self.parser_state = CssAttribute;
            ret Element(~"*");
        }

        self.input_state.unget(c);
        let element = self.input_state.parse_ident();

        self.parser_state = CssAttribute;

        ret Element(element);
    }

    fn parse_css_attribute(c : u8) -> Token {
        let mut ch = c;

        /* If we've reached the end of this list of attributes,
        look for the relation to the next element.*/
        if c.is_whitespace() {
            self.parser_state = CssRelation;
            self.input_state.eat_whitespace();

            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"File ended before description of style" }
            }

            ret self.parse_css_relation(ch);
        }

        alt ch {
          '.' as u8 { ret Attr(style::Includes(~"class", self.input_state.parse_ident())); }
          '#' as u8 { ret Attr(style::Includes(~"id", self.input_state.parse_ident())); }
          '[' as u8 {
            let attr_name = self.input_state.parse_ident();

            alt self.input_state.get() {
              CoeChar(c)    { ch = c; }
              CoeEof        { fail ~"File ended before description finished"; }
            }

            if ch == ']' as u8 {
                ret Attr(style::Exists(attr_name));
            } else if ch == '=' as u8 {
                let attr_val = self.input_state.parse_ident();
                self.input_state.expect(']' as u8);
                ret Attr(style::Exact(attr_name, attr_val));
            } else if ch == '~' as u8 {
                self.input_state.expect('=' as u8);
                let attr_val = self.input_state.parse_ident();
                self.input_state.expect(']' as u8);
                ret Attr(style::Includes(attr_name, attr_val));
            } else if ch == '|' as u8 {
                self.input_state.expect('=' as u8);
                let attr_val = self.input_state.parse_ident();
                self.input_state.expect(']' as u8);
                ret Attr(style::StartsWith(attr_name, attr_val));
            }

            fail #fmt("Unexpected symbol %c in attribute", ch as char);
          }
          _   { fail #fmt("Unexpected symbol %c in attribute", ch as char); }
        }
    }

    fn parse_css_description(c: u8) -> Token {
        let mut ch = c;

        if ch == '}' as u8 {
            self.parser_state = CssElement;
            self.input_state.eat_whitespace();
            ret EndDescription;
        } else if ch.is_whitespace() {
            self.input_state.eat_whitespace();

            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"Reached end of file in CSS description" }
            }
        }

        let mut desc_name = ~[];

        // Get the name of the descriptor
        loop {
            if ch.is_whitespace() {
                self.input_state.eat_whitespace();
            } else if ch == ':' as u8 {
                if desc_name.len() == 0u {
                    fail ~"Expected descriptor name";
                } else {
                    break;
                }
            } else {
                push(desc_name, ch);
            }

            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"Reached end of file in CSS description" }
            }
        }

        self.input_state.eat_whitespace();
        let mut desc_val = ~[];

        // Get the value of the descriptor
        loop {
            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"Reached end of file in CSS description" }
            }

            if ch.is_whitespace() {
                self.input_state.eat_whitespace();
            } else if ch == '}' as u8 {
                if desc_val.len() == 0u {
                    fail ~"Expected descriptor value";
                } else {
                    self.input_state.unget('}' as u8);
                    break;
                }
            } else if ch == ';' as u8 {
                if desc_val.len() == 0u {
                    fail ~"Expected descriptor value";
                } else {
                    break;
                }
            } else {
                push(desc_val, ch);
            }
        }

        ret Description(from_bytes(desc_name), from_bytes(desc_val));
    }
}

fn parser(reader: io::reader, state : ParserState) -> CssLexer {
    ret { input_state: {mut lookahead: none, reader: reader}, mut parser_state: state };
}

fn lex_css_from_bytes(-content : ~[u8], result_chan : chan<Token>) {
    let reader = io::bytes_reader(content);
    let lexer = parser(reader, CssElement);

    loop {
        let token = lexer.parse_css();
        let should_break = (token == Eof);

        result_chan.send(token);

        if should_break {
            break;
        }
    }
}

fn spawn_css_lexer_from_string(-content : ~str) -> port<Token> {
    let result_port = port();
    let result_chan = chan(result_port);

    task::spawn(|| lex_css_from_bytes(str::bytes(content), result_chan));

    ret result_port;
}

#[warn(no_non_implicitly_copyable_typarams)]
fn spawn_css_lexer_from_file(-filename: ~str) -> port<Token> {
    let result_port = port();
    let result_chan = chan(result_port);

    task::spawn(|| {
        assert (copy filename).ends_with(~".css");
        let file_try = io::read_whole_file(filename);

        // Check if the given css file existed, if it does, parse it,
        // otherwise just send an eof.
        if file_try.is_ok() {
            #debug["Lexing css sheet %?", filename];
            let file_data = file_try.get();
            lex_css_from_bytes(file_data, result_chan);
        } else {
            #debug["Failed to open css sheet %?", filename];
            result_chan.send(Eof);
        }
    });

    ret result_port;
}
<commit_msg>Get rid of ~strs<commit_after>#[doc = "Code to lex and tokenize css files."]

import comm::{port, chan};
import dom::style;
import option::is_none;
import str::from_bytes;
import vec::push;

import lexer_util::*;

enum ParserState {
    CssElement,
    CssRelation,
    CssDescription,
    CssAttribute
}

type CssLexer = {
    input_state: InputState,
    mut parser_state: ParserState
};

enum Token {
    StartDescription,
    EndDescription,
    Descendant,
    Child,
    Sibling,
    Comma,
    Element(~str),
    Attr(style::Attr),
    Description(~str, ~str),
    Eof
}

trait css_methods {
    fn parse_css() -> Token;
    fn parse_css_relation(c : u8) -> Token;
    fn parse_css_element(c : u8) -> Token;
    fn parse_css_attribute(c : u8) -> Token;
    fn parse_css_description(c: u8) -> Token;
}

impl css_methods of css_methods for CssLexer {
    fn parse_css() -> Token {
        let mut ch: u8;
        alt self.input_state.get() {
            CoeChar(c) { ch = c; }
            CoeEof { ret Eof; }
        }

        let token = alt self.parser_state {
          CssDescription { self.parse_css_description(ch) }
          CssAttribute   { self.parse_css_attribute(ch) }
          CssElement     { self.parse_css_element(ch) }
          CssRelation    { self.parse_css_relation(ch) }
        };

        #debug["token=%?", token];
        ret token;
    }

    fn parse_css_relation(c : u8) -> Token {
        self.parser_state = CssElement;

        let token = alt c {
          '{' as u8  { self.parser_state = CssDescription; StartDescription }
          '>' as u8  { Child }
          '+' as u8  { Sibling }
          ',' as u8  { Comma }
          _          { self.input_state.unget(c); Descendant }
        };

        self.input_state.eat_whitespace();

        ret token;
    }

    fn parse_css_element(c : u8) -> Token {
        assert is_none(self.input_state.lookahead);

        /* Check for special attributes with an implied element,
        or a wildcard which is not a alphabet character.*/
        if c == '.' as u8 || c == '#' as u8 {
            self.parser_state = CssAttribute;
            self.input_state.unget(c);
            ret Element(~"*");
        } else if c == '*' as u8 {
            self.parser_state = CssAttribute;
            ret Element(~"*");
        }

        self.input_state.unget(c);
        let element = self.input_state.parse_ident();

        self.parser_state = CssAttribute;

        ret Element(element);
    }

    fn parse_css_attribute(c : u8) -> Token {
        let mut ch = c;

        /* If we've reached the end of this list of attributes,
        look for the relation to the next element.*/
        if c.is_whitespace() {
            self.parser_state = CssRelation;
            self.input_state.eat_whitespace();

            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"File ended before description of style" }
            }

            ret self.parse_css_relation(ch);
        }

        alt ch {
          '.' as u8 { ret Attr(style::Includes(~"class", self.input_state.parse_ident())); }
          '#' as u8 { ret Attr(style::Includes(~"id", self.input_state.parse_ident())); }
          '[' as u8 {
            let attr_name = self.input_state.parse_ident();

            alt self.input_state.get() {
              CoeChar(c)    { ch = c; }
              CoeEof        { fail ~"File ended before description finished"; }
            }

            if ch == ']' as u8 {
                ret Attr(style::Exists(attr_name));
            } else if ch == '=' as u8 {
                let attr_val = self.input_state.parse_ident();
                self.input_state.expect(']' as u8);
                ret Attr(style::Exact(attr_name, attr_val));
            } else if ch == '~' as u8 {
                self.input_state.expect('=' as u8);
                let attr_val = self.input_state.parse_ident();
                self.input_state.expect(']' as u8);
                ret Attr(style::Includes(attr_name, attr_val));
            } else if ch == '|' as u8 {
                self.input_state.expect('=' as u8);
                let attr_val = self.input_state.parse_ident();
                self.input_state.expect(']' as u8);
                ret Attr(style::StartsWith(attr_name, attr_val));
            }

            fail #fmt("Unexpected symbol %c in attribute", ch as char);
          }
          _   { fail #fmt("Unexpected symbol %c in attribute", ch as char); }
        }
    }

    fn parse_css_description(c: u8) -> Token {
        let mut ch = c;

        if ch == '}' as u8 {
            self.parser_state = CssElement;
            self.input_state.eat_whitespace();
            ret EndDescription;
        } else if ch.is_whitespace() {
            self.input_state.eat_whitespace();

            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"Reached end of file in CSS description" }
            }
        }

        let mut desc_name = ~[];

        // Get the name of the descriptor
        loop {
            if ch.is_whitespace() {
                self.input_state.eat_whitespace();
            } else if ch == ':' as u8 {
                if desc_name.len() == 0u {
                    fail ~"Expected descriptor name";
                } else {
                    break;
                }
            } else {
                push(desc_name, ch);
            }

            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"Reached end of file in CSS description" }
            }
        }

        self.input_state.eat_whitespace();
        let mut desc_val = ~[];

        // Get the value of the descriptor
        loop {
            alt self.input_state.get() {
              CoeChar(c)  { ch = c }
              CoeEof      { fail ~"Reached end of file in CSS description" }
            }

            if ch.is_whitespace() {
                self.input_state.eat_whitespace();
            } else if ch == '}' as u8 {
                if desc_val.len() == 0u {
                    fail ~"Expected descriptor value";
                } else {
                    self.input_state.unget('}' as u8);
                    break;
                }
            } else if ch == ';' as u8 {
                if desc_val.len() == 0u {
                    fail ~"Expected descriptor value";
                } else {
                    break;
                }
            } else {
                push(desc_val, ch);
            }
        }

        ret Description(from_bytes(desc_name), from_bytes(desc_val));
    }
}

fn parser(reader: io::reader, state : ParserState) -> CssLexer {
    ret { input_state: {mut lookahead: none, reader: reader}, mut parser_state: state };
}

fn lex_css_from_bytes(-content : ~[u8], result_chan : chan<Token>) {
    let reader = io::bytes_reader(content);
    let lexer = parser(reader, CssElement);

    loop {
        let token = lexer.parse_css();
        let should_break = (token == Eof);

        result_chan.send(token);

        if should_break {
            break;
        }
    }
}

fn spawn_css_lexer_from_string(-content : ~str) -> port<Token> {
    let result_port = port();
    let result_chan = chan(result_port);

    task::spawn(|| lex_css_from_bytes(str::bytes(content), result_chan));

    ret result_port;
}

#[warn(no_non_implicitly_copyable_typarams)]
fn spawn_css_lexer_from_file(-filename: ~str) -> port<Token> {
    let result_port = port();
    let result_chan = chan(result_port);

    task::spawn(|| {
        assert filename.ends_with(".css");
        let file_try = io::read_whole_file(filename);

        // Check if the given css file existed, if it does, parse it,
        // otherwise just send an eof.
        if file_try.is_ok() {
            #debug["Lexing css sheet %?", filename];
            let file_data = file_try.get();
            lex_css_from_bytes(file_data, result_chan);
        } else {
            #debug["Failed to open css sheet %?", filename];
            result_chan.send(Eof);
        }
    });

    ret result_port;
}
<|endoftext|>


<commit_before>use crate::thread::Time;
use crate::*;
use rustc_target::abi::{Align, Size};
use std::time::{Instant, SystemTime};

/// Implementation of the SYS_futex syscall.
pub fn futex<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    args: &[OpTy<'tcx, Tag>],
    dest: PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    // The amount of arguments used depends on the type of futex operation.
    // The full futex syscall takes six arguments (excluding the syscall
    // number), which is also the maximum amount of arguments a linux syscall
    // can take on most architectures.
    // However, not all futex operations use all six arguments. The unused ones
    // may or may not be left out from the `syscall()` call.
    // Therefore we don't use `check_arg_count` here, but only check for the
    // number of arguments to fall within a range.
    if !(4..=7).contains(&args.len()) {
        throw_ub_format!("incorrect number of arguments for futex syscall: got {}, expected between 4 and 7 (inclusive)", args.len());
    }

    // The first three arguments (after the syscall number itself) are the same to all futex operations:
    //     (int *addr, int op, int val).
    // We checked above that these definitely exist.
    //
    // `addr` is used to identify the mutex, but note that not all futex
    // operations actually read from this addres or even require this address
    // to exist. Also, the type of `addr` is not consistent. The API requires
    // it to be a 4-byte aligned pointer, and will use the 4 bytes at the given
    // address as an (atomic) i32. It's not uncommon for `addr` to be passed as
    // another type than `*mut i32`, such as `*const AtomicI32`.
    let addr = this.force_ptr(this.read_scalar(args[1])?.check_init()?)?;
    let op = this.read_scalar(args[2])?.to_i32()?;
    let val = this.read_scalar(args[3])?.to_i32()?;

    let thread = this.get_active_thread();

    let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?;
    let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?;
    let futex_wake = this.eval_libc_i32("FUTEX_WAKE")?;
    let futex_realtime = this.eval_libc_i32("FUTEX_CLOCK_REALTIME")?;

    // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
    // Miri doesn't support that anyway, so we ignore that flag.
    match op & !futex_private {
        // FUTEX_WAIT: (int *addr, int op = FUTEX_WAIT, int val, const timespec *timeout)
        // Blocks the thread if *addr still equals val. Wakes up when FUTEX_WAKE is called on the same address,
        // or *timeout expires. `timeout == null` for an infinite timeout.
        op if op & !futex_realtime == futex_wait => {
            if args.len() < 5 {
                throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
            }
            let timeout = args[4];
            let timeout_time = if this.is_null(this.read_scalar(timeout)?.check_init()?)? {
                None
            } else {
                let duration = match this.read_timespec(timeout)? {
                    Some(duration) => duration,
                    None => {
                        let einval = this.eval_libc("EINVAL")?;
                        this.set_last_error(einval)?;
                        this.write_scalar(Scalar::from_machine_isize(-1, this), dest)?;
                        return Ok(());
                    }
                };
                this.check_no_isolation("FUTEX_WAIT with timeout")?;
                Some(if op & futex_realtime != 0 {
                    Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
                } else {
                    Time::Monotonic(Instant::now().checked_add(duration).unwrap())
                })
            };
            // Check the pointer for alignment and validity.
            // Atomic operations are only available for fully aligned values.
            this.memory.check_ptr_access(addr.into(), Size::from_bytes(4), Align::from_bytes(4).unwrap())?;
            // Read an `i32` through the pointer, regardless of any wrapper types (e.g. `AtomicI32`).
            let futex_val = this.memory.get_raw(addr.alloc_id)?.read_scalar(this, addr, Size::from_bytes(4))?.to_i32()?;
            if val == futex_val {
                // The value still matches, so we block the trait make it wait for FUTEX_WAKE.
                this.block_thread(thread);
                this.futex_wait(addr, thread);
                // Succesfully waking up from FUTEX_WAIT always returns zero.
                this.write_scalar(Scalar::from_machine_isize(0, this), dest)?;
                // Register a timeout callback if a timeout was specified.
                // This callback will override the return value when the timeout triggers.
                if let Some(timeout_time) = timeout_time {
                    this.register_timeout_callback(
                        thread,
                        timeout_time,
                        Box::new(move |this| {
                            this.unblock_thread(thread);
                            this.futex_remove_waiter(addr, thread);
                            let etimedout = this.eval_libc("ETIMEDOUT")?;
                            this.set_last_error(etimedout)?;
                            this.write_scalar(Scalar::from_machine_isize(-1, this), dest)?;
                            Ok(())
                        }),
                    );
                }
            } else {
                // The futex value doesn't match the expected value, so we return failure
                // right away without sleeping: -1 and errno set to EAGAIN.
                let eagain = this.eval_libc("EAGAIN")?;
                this.set_last_error(eagain)?;
                this.write_scalar(Scalar::from_machine_isize(-1, this), dest)?;
            }
        }
        // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
        // Wakes at most `val` threads waiting on the futex at `addr`.
        // Returns the amount of threads woken up.
        // Does not access the futex value at *addr.
        op if op == futex_wake => {
            let mut n = 0;
            for _ in 0..val {
                if let Some(thread) = this.futex_wake(addr) {
                    this.unblock_thread(thread);
                    this.unregister_timeout_callback_if_exists(thread);
                    n += 1;
                } else {
                    break;
                }
            }
            this.write_scalar(Scalar::from_machine_isize(n, this), dest)?;
        }
        op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),
    }

    Ok(())
}
<commit_msg>Use read_scalar_at_offset in futex_wait instead of memory.get_raw.<commit_after>use crate::thread::Time;
use crate::*;
use rustc_target::abi::{Align, Size};
use std::time::{Instant, SystemTime};

/// Implementation of the SYS_futex syscall.
pub fn futex<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    args: &[OpTy<'tcx, Tag>],
    dest: PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    // The amount of arguments used depends on the type of futex operation.
    // The full futex syscall takes six arguments (excluding the syscall
    // number), which is also the maximum amount of arguments a linux syscall
    // can take on most architectures.
    // However, not all futex operations use all six arguments. The unused ones
    // may or may not be left out from the `syscall()` call.
    // Therefore we don't use `check_arg_count` here, but only check for the
    // number of arguments to fall within a range.
    if !(4..=7).contains(&args.len()) {
        throw_ub_format!("incorrect number of arguments for futex syscall: got {}, expected between 4 and 7 (inclusive)", args.len());
    }

    // The first three arguments (after the syscall number itself) are the same to all futex operations:
    //     (int *addr, int op, int val).
    // We checked above that these definitely exist.
    let addr = this.read_immediate(args[1])?;
    let op = this.read_scalar(args[2])?.to_i32()?;
    let val = this.read_scalar(args[3])?.to_i32()?;

    // The raw pointer value is used to identify the mutex.
    // Not all mutex operations actually read from this address or even require this address to exist.
    let futex_ptr = this.force_ptr(addr.to_scalar()?)?;

    let thread = this.get_active_thread();

    let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?;
    let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?;
    let futex_wake = this.eval_libc_i32("FUTEX_WAKE")?;
    let futex_realtime = this.eval_libc_i32("FUTEX_CLOCK_REALTIME")?;

    // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
    // Miri doesn't support that anyway, so we ignore that flag.
    match op & !futex_private {
        // FUTEX_WAIT: (int *addr, int op = FUTEX_WAIT, int val, const timespec *timeout)
        // Blocks the thread if *addr still equals val. Wakes up when FUTEX_WAKE is called on the same address,
        // or *timeout expires. `timeout == null` for an infinite timeout.
        op if op & !futex_realtime == futex_wait => {
            if args.len() < 5 {
                throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
            }
            let timeout = args[4];
            let timeout_time = if this.is_null(this.read_scalar(timeout)?.check_init()?)? {
                None
            } else {
                let duration = match this.read_timespec(timeout)? {
                    Some(duration) => duration,
                    None => {
                        let einval = this.eval_libc("EINVAL")?;
                        this.set_last_error(einval)?;
                        this.write_scalar(Scalar::from_machine_isize(-1, this), dest)?;
                        return Ok(());
                    }
                };
                this.check_no_isolation("FUTEX_WAIT with timeout")?;
                Some(if op & futex_realtime != 0 {
                    Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
                } else {
                    Time::Monotonic(Instant::now().checked_add(duration).unwrap())
                })
            };
            // Check the pointer for alignment and validity.
            // The API requires `addr` to be a 4-byte aligned pointer, and will
            // use the 4 bytes at the given address as an (atomic) i32.
            this.memory.check_ptr_access(addr.to_scalar()?, Size::from_bytes(4), Align::from_bytes(4).unwrap())?;
            // Read an `i32` through the pointer, regardless of any wrapper types.
            // It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
            let futex_val = this.read_scalar_at_offset(addr.into(), 0, this.machine.layouts.i32)?.to_i32()?;
            if val == futex_val {
                // The value still matches, so we block the trait make it wait for FUTEX_WAKE.
                this.block_thread(thread);
                this.futex_wait(futex_ptr, thread);
                // Succesfully waking up from FUTEX_WAIT always returns zero.
                this.write_scalar(Scalar::from_machine_isize(0, this), dest)?;
                // Register a timeout callback if a timeout was specified.
                // This callback will override the return value when the timeout triggers.
                if let Some(timeout_time) = timeout_time {
                    this.register_timeout_callback(
                        thread,
                        timeout_time,
                        Box::new(move |this| {
                            this.unblock_thread(thread);
                            this.futex_remove_waiter(futex_ptr, thread);
                            let etimedout = this.eval_libc("ETIMEDOUT")?;
                            this.set_last_error(etimedout)?;
                            this.write_scalar(Scalar::from_machine_isize(-1, this), dest)?;
                            Ok(())
                        }),
                    );
                }
            } else {
                // The futex value doesn't match the expected value, so we return failure
                // right away without sleeping: -1 and errno set to EAGAIN.
                let eagain = this.eval_libc("EAGAIN")?;
                this.set_last_error(eagain)?;
                this.write_scalar(Scalar::from_machine_isize(-1, this), dest)?;
            }
        }
        // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
        // Wakes at most `val` threads waiting on the futex at `addr`.
        // Returns the amount of threads woken up.
        // Does not access the futex value at *addr.
        op if op == futex_wake => {
            let mut n = 0;
            for _ in 0..val {
                if let Some(thread) = this.futex_wake(futex_ptr) {
                    this.unblock_thread(thread);
                    this.unregister_timeout_callback_if_exists(thread);
                    n += 1;
                } else {
                    break;
                }
            }
            this.write_scalar(Scalar::from_machine_isize(n, this), dest)?;
        }
        op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),
    }

    Ok(())
}
<|endoftext|>