完成基本功能

This commit is contained in:
MeiK 2020-10-26 17:40:36 +08:00
parent b2961c34fe
commit 11c10cc24d
5 changed files with 186 additions and 3 deletions

View File

@ -24,4 +24,12 @@
## TODOs
- 修改为文件形式的输入输出,为了这部分的优化我浪费了太多的时间,最后发现过早的优化果然是万恶之源。
已经完成基本功能,后续需要优化
- 基于 ptrace 的精准内存测量
- 基于 cgroups 的资源控制
- 用户、组限制
- 示例代码
- 安全测试
- 优化 args 生成代码,减少测量出的用户代码执行时间
- special judge

View File

@ -23,6 +23,8 @@ pub enum Error {
UnknownRequestData,
RequestDataNotFound,
SyscallError(String),
OpenFileError(PathBuf, io::Error),
ReadFileError(PathBuf, io::Error),
CustomError(String),
}

View File

@ -2,12 +2,14 @@ use super::config::{STDERR_FILENAME, STDOUT_FILENAME};
use super::error::{Error, Result};
use super::process::Process;
use super::runner::RunnerStatus;
use crate::result::stanard_result;
use crate::river::judge_response::State;
use crate::river::Language;
use crate::river::{CompileData, JudgeData, JudgeResult, JudgeStatus};
use crate::river::{JudgeRequest, JudgeResponse};
use std::path::Path;
use tokio::fs;
use tokio::io::AsyncReadExt;
impl JudgeResponse {
fn new() -> JudgeResponse {
@ -76,9 +78,17 @@ pub async fn judger(
resp.errmsg = format!("Exceptional program return code: {}", status.exit_code);
resp.state = Some(State::Result(JudgeResult::RuntimeError as i32));
} else {
// TODO: AC\WA\PE
// 没有 ole 这种操作,之前 ole 就是错的
resp.state = Some(State::Result(JudgeResult::Accepted as i32));
let mut file = match fs::File::open(path.join(STDOUT_FILENAME)).await {
Ok(val) => val,
Err(e) => return Err(Error::OpenFileError(path.join(STDOUT_FILENAME), e)),
};
let mut out = Vec::new();
if let Err(e) = file.read_to_end(&mut out).await {
return Err(Error::ReadFileError(path.join(STDOUT_FILENAME), e));
};
let result = stanard_result(&out, &data.out_data)?;
resp.state = Some(State::Result(result as i32));
}
Ok(resp)
}

View File

@ -20,6 +20,7 @@ mod error;
mod exec_args;
mod judger;
mod process;
mod result;
mod runner;
mod seccomp;

162
src/result.rs Normal file
View File

@ -0,0 +1,162 @@
use super::error::Result;
use crate::river::JudgeResult;
pub fn stanard_result(out: &[u8], ans: &[u8]) -> Result<JudgeResult> {
let out_len = out.len();
let ans_len = ans.len();
let mut out_offset = 0;
let mut ans_offset = 0;
// 没有 PEPE 直接 WA
let mut r = JudgeResult::Accepted;
while out_offset < out_len && ans_offset < ans_len {
let (out_start, out_end, out_exists) = next_line(&out, out_offset, out_len);
let (ans_start, ans_end, ans_exists) = next_line(&ans, ans_offset, ans_len);
if !out_exists || !ans_exists {
// 如果一个已经读取完但另一个还有数据,则结果为 WA
if out_exists != ans_exists {
r = JudgeResult::WrongAnswer;
}
break;
}
// 如果两个数据当前行长度不同,则结果为 WA这个长度已经排除了末尾空白符号
if out_end - out_start != ans_end - ans_start {
r = JudgeResult::WrongAnswer;
break;
}
let line_len = out_end - out_start;
for i in 0..line_len {
// 逐个对比
if out[out_start + i] != ans[ans_start + i] {
r = JudgeResult::WrongAnswer;
break;
}
}
// 如果结果出来了,则退出循环
if r != JudgeResult::Accepted {
break;
}
out_offset = out_end;
ans_offset = ans_end;
}
Ok(r)
}
/**
*
*
* "Hello ; "
* " "
* " World"
* -----------------
* "Hello ;"
* " World"
*/
fn next_line(v: &[u8], offset: usize, len: usize) -> (usize, usize, bool) {
let mut line_offset = offset;
let mut left = 0;
let mut right = len;
let mut has_line = false;
while line_offset < len {
let ch = v[line_offset] as char;
// 当读取到某行结束时
if ch == '\n' {
if has_line {
// 如果已经有新行的数据,则在这个位置结束
right = line_offset;
break;
} else {
// 如果还没有数据,说明整行为空,忽略当前行,将下一行设为起点重复过程
left = line_offset + 1;
}
}
if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
// 空白字符
} else {
// 非空白字符
has_line = true;
}
line_offset += 1;
}
// 排除该行末尾的空白字符
while left < right {
let ch = v[right - 1] as char;
if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
// 空白字符
} else {
// 非空白字符
break;
}
right -= 1;
}
(left, right, has_line)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::river::JudgeResult;
#[test]
fn test1() {
let v: &[u8] = "Hello World!".as_bytes();
let (l, r, e) = next_line(v, 0, v.len());
assert_eq!(l, 0);
assert_eq!(r, 12);
assert!(e);
let (_l, _r, e) = next_line(v, r, v.len());
assert!(!e);
}
#[test]
fn test2() {
let v: &[u8] = "Hello World! ".as_bytes();
let (l, r, e) = next_line(v, 0, v.len());
assert_eq!(l, 0);
assert_eq!(r, 12);
assert!(e);
}
#[test]
fn test3() {
let v: &[u8] = " Hello World!".as_bytes();
let (l, r, e) = next_line(v, 0, v.len());
assert_eq!(l, 0);
assert_eq!(r, 15);
assert!(e);
}
#[test]
fn test4() {
let v: &[u8] = " Hello World!\n Hello World!\n\n\n\n \n\n\n\n\n\n \t\t\t \t\n\n\n\n".as_bytes();
let (l, r, e) = next_line(v, 0, v.len());
assert_eq!(l, 0);
assert_eq!(r, 15);
assert!(e);
let (l, r, e) = next_line(v, r, v.len());
assert_eq!(l, 16);
assert_eq!(r, 31);
assert!(e);
let (_l, _r, e) = next_line(v, r, v.len());
assert!(!e);
}
#[test]
fn test5() {
let ans: &[u8] = "Hello World!".as_bytes();
let out: &[u8] = "Hello World!".as_bytes();
assert_eq!(stanard_result(out, ans).unwrap(), JudgeResult::Accepted);
}
#[test]
fn test6() {
let ans: &[u8] = "Hello World!".as_bytes();
let out: &[u8] = "Hello World! ".as_bytes();
assert_eq!(stanard_result(out, ans).unwrap(), JudgeResult::Accepted);
}
#[test]
fn test7() {
let ans: &[u8] = "Hello World! \n\n\n\n \n\n\n\n".as_bytes();
let out: &[u8] = "Hello World!\t\t\t\t\n\n\n\n \n\n\n\n\t\t\t\t".as_bytes();
assert_eq!(stanard_result(out, ans).unwrap(), JudgeResult::Accepted);
}
}