core: 支持显示时空限制 (#65)

虽然有点丑,但是能用
This commit is contained in:
undefined committed 2021-04-10 18:44:07 +08:00
1 parent d5c5b3fd47
commit 1c89596cb3
20 files changed
+84 -59

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@hydrooj/hydrojudge", "name": "@hydrooj/hydrojudge",
"bin": "bin/hydrojudge.js", "bin": "bin/hydrojudge.js",
"version": "2.4.19", "version": "2.5.0",
"os": [ "os": [
"linux" "linux"
], ],
+7 -7
View File
@@ -88,8 +88,8 @@ async function read0(folder: string, files: string[], checkFile, cfg) {
const config = { const config = {
count: 0, count: 0,
subtasks: [{ subtasks: [{
time_limit_ms: parseTimeMS(cfg.time || '1s'), time: parseTimeMS(cfg.time || '1s'),
memory_limit_mb: parseMemoryMB(cfg.memory || '256m'), memory: parseMemoryMB(cfg.memory || '256m'),
type: 'sum', type: 'sum',
cases: [], cases: [],
score: Math.floor(100 / cases.length), score: Math.floor(100 / cases.length),
@@ -105,8 +105,8 @@ async function read0(folder: string, files: string[], checkFile, cfg) {
} }
if (extra < cases.length) { if (extra < cases.length) {
config.subtasks.push({ config.subtasks.push({
time_limit_ms: parseTimeMS(cfg.time || '1s'), time: parseTimeMS(cfg.time || '1s'),
memory_limit_mb: parseMemoryMB(cfg.memory || '256m'), memory: parseMemoryMB(cfg.memory || '256m'),
type: 'sum', type: 'sum',
cases: [], cases: [],
score: Math.floor(100 / cases.length) + 1, score: Math.floor(100 / cases.length) + 1,
@@ -134,8 +134,8 @@ async function read1(folder: string, files: string[], checkFile, cfg) {
if (fs.existsSync(path.resolve(folder, c.output))) { if (fs.existsSync(path.resolve(folder, c.output))) {
if (!subtask[REG.subtask(data)]) { if (!subtask[REG.subtask(data)]) {
subtask[REG.subtask(data)] = [{ subtask[REG.subtask(data)] = [{
time_limit_ms: parseTimeMS(cfg.time || '1s'), time: parseTimeMS(cfg.time || '1s'),
memory_limit_mb: parseMemoryMB(cfg.memory || '256m'), memory: parseMemoryMB(cfg.memory || '256m'),
type: 'min', type: 'min',
cases: [c], cases: [c],
}]; }];
@@ -199,7 +199,7 @@ function isValidConfig(config) {
if (config.count > (getConfig('testcases_max') || 100)) { if (config.count > (getConfig('testcases_max') || 100)) {
throw new FormatError('Too many testcases. Cancelled.'); throw new FormatError('Too many testcases. Cancelled.');
} }
const total_time = sum(config.subtasks.map((subtask) => subtask.time_limit_ms * subtask.cases.length)); const total_time = sum(config.subtasks.map((subtask) => subtask.time * subtask.cases.length));
if (total_time > (getConfig('total_time_limit') || 60) * 1000) { if (total_time > (getConfig('total_time_limit') || 60) * 1000) {
throw new FormatError('Total time limit longer than {0}s. Cancelled.', [+getConfig('total_time_limit') || 60]); throw new FormatError('Total time limit longer than {0}s. Cancelled.', [+getConfig('total_time_limit') || 60]);
} }
+4 -4
View File
@@ -50,8 +50,8 @@ function judgeCase(c, sid) {
stderr, stderr,
copyIn, copyIn,
copyOut, copyOut,
time_limit_ms: ctxSubtask.subtask.time_limit_ms * ctx.execute.time, time: ctxSubtask.subtask.time * ctx.execute.time,
memory_limit_mb: ctxSubtask.subtask.memory_limit_mb, memory: ctxSubtask.subtask.memory,
}, },
); );
const { code, time_usage_ms, memory_usage_kb } = res; const { code, time_usage_ms, memory_usage_kb } = res;
@@ -62,9 +62,9 @@ function judgeCase(c, sid) {
let message: any = ''; let message: any = '';
let score = 0; let score = 0;
if (status === STATUS.STATUS_ACCEPTED) { if (status === STATUS.STATUS_ACCEPTED) {
if (time_usage_ms > ctxSubtask.subtask.time_limit_ms * ctx.execute.time) { if (time_usage_ms > ctxSubtask.subtask.time * ctx.execute.time) {
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED; status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
} else if (memory_usage_kb > ctxSubtask.subtask.memory_limit_mb * 1024) { } else if (memory_usage_kb > ctxSubtask.subtask.memory * 1024) {
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED; status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
} else { } else {
[status, score, message] = await check({ [status, score, message] = await check({
+2 -2
View File
@@ -77,8 +77,8 @@ export const judge = async (ctx) => {
stderr, stderr,
copyIn: execute.copyIn, copyIn: execute.copyIn,
copyOut, copyOut,
time_limit_ms: 1000, time: 1000,
memory_limit_mb: 512, memory: 512,
}, },
); );
const { code, time_usage_ms, memory_usage_kb } = res; const { code, time_usage_ms, memory_usage_kb } = res;
+6 -6
View File
@@ -24,21 +24,21 @@ function judgeCase(c) {
{ {
execute: ctx.executeUser.execute.replace(/\$\{name\}/g, 'code'), execute: ctx.executeUser.execute.replace(/\$\{name\}/g, 'code'),
copyIn: ctx.executeUser.copyIn, copyIn: ctx.executeUser.copyIn,
time_limit_ms: ctxSubtask.subtask.time_limit_ms, time: ctxSubtask.subtask.time,
memory_limit_mb: ctxSubtask.subtask.memory_limit_mb, memory: ctxSubtask.subtask.memory,
}, { }, {
execute: `${ctx.executeInteractor.execute.replace(/\$\{name\}/g, 'interactor')} /w/in /w/tout`, execute: `${ctx.executeInteractor.execute.replace(/\$\{name\}/g, 'interactor')} /w/in /w/tout`,
copyIn: ctx.executeInteractor.copyIn, copyIn: ctx.executeInteractor.copyIn,
time_limit_ms: ctxSubtask.subtask.time_limit_ms * 2, time: ctxSubtask.subtask.time * 2,
memory_limit_mb: ctxSubtask.subtask.memory_limit_mb * 2, memory: ctxSubtask.subtask.memory * 2,
}, },
]); ]);
let status; let status;
let score = 0; let score = 0;
let message: any = ''; let message: any = '';
if (time_usage_ms > ctxSubtask.subtask.time_limit_ms) { if (time_usage_ms > ctxSubtask.subtask.time) {
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED; status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
} else if (memory_usage_kb > ctxSubtask.subtask.memory_limit_mb * 1024) { } else if (memory_usage_kb > ctxSubtask.subtask.memory * 1024) {
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED; status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
} else if (code) { } else if (code) {
status = STATUS.STATUS_RUNTIME_ERROR; status = STATUS.STATUS_RUNTIME_ERROR;
+2 -2
View File
@@ -69,8 +69,8 @@ export const judge = async (ctx) => {
stderr, stderr,
copyIn, copyIn,
copyOut, copyOut,
time_limit_ms: parseTimeMS(ctx.config.time || '1s'), time: parseTimeMS(ctx.config.time || '1s'),
memory_limit_mb: parseMemoryMB(ctx.config.memory || '128m'), memory: parseMemoryMB(ctx.config.memory || '128m'),
}, },
); );
const { code, time_usage_ms, memory_usage_kb } = res; const { code, time_usage_ms, memory_usage_kb } = res;
+6 -6
View File
@@ -25,8 +25,8 @@ const statusMap = {
function proc({ function proc({
execute = '', execute = '',
time_limit_ms = 16000, time = 16000,
memory_limit_mb = 1024, memory = 1024,
process_limit = 32, process_limit = 32,
stdin = '', copyIn = {}, copyOut = [], copyOutCached = [], stdin = '', copyIn = {}, copyOut = [], copyOutCached = [],
} = {}) { } = {}) {
@@ -39,9 +39,9 @@ function proc({
{ name: 'stdout', max: 1024 * 1024 * size }, { name: 'stdout', max: 1024 * 1024 * size },
{ name: 'stderr', max: 1024 * 1024 * size }, { name: 'stderr', max: 1024 * 1024 * size },
], ],
cpuLimit: time_limit_ms * 1000 * 1000, cpuLimit: time * 1000 * 1000,
realCpuLimit: time_limit_ms * 3000 * 1000, realCpuLimit: time * 3000 * 1000,
memoryLimit: memory_limit_mb * 1024 * 1024, memoryLimit: memory * 1024 * 1024,
procLimit: process_limit, procLimit: process_limit,
copyIn, copyIn,
copyOut, copyOut,
@@ -58,7 +58,7 @@ async function adaptResult(result, params) {
files: result.files, files: result.files,
code: result.exitStatus, code: result.exitStatus,
}; };
if (ret.time_usage_ms >= (params.time_limit_ms || 16000)) { if (ret.time_usage_ms >= (params.time || 16000)) {
ret.status = STATUS.STATUS_TIME_LIMIT_EXCEEDED; ret.status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
} }
ret.files = result.files || {}; ret.files = result.files || {};
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "hydrooj", "name": "hydrooj",
"version": "2.21.19", "version": "2.22.0",
"bin": "bin/hydrooj.js", "bin": "bin/hydrooj.js",
"main": "dist/loader.js", "main": "dist/loader.js",
"typings": "dist/loader.d.ts", "typings": "dist/loader.d.ts",
+7
View File
@@ -11,6 +11,7 @@ import {
import paginate from '../lib/paginate'; import paginate from '../lib/paginate';
import { isPid } from '../lib/validator'; import { isPid } from '../lib/validator';
import difficultyAlgorithm from '../lib/difficulty'; import difficultyAlgorithm from '../lib/difficulty';
import { parseConfig } from '../lib/testdataConfig';
import * as system from '../model/system'; import * as system from '../model/system';
import problem from '../model/problem'; import problem from '../model/problem';
import record from '../model/record'; import record from '../model/record';
@@ -189,6 +190,12 @@ export class ProblemDetailHandler extends ProblemHandler {
// e.g. ![img](a.jpg) will navigate to ![img](./pid/file/a.jpg) // e.g. ![img](a.jpg) will navigate to ![img](./pid/file/a.jpg)
this.response.body.pdoc.content = this.response.body.pdoc.content this.response.body.pdoc.content = this.response.body.pdoc.content
.replace(/\(file:\/\//g, `(./${this.pdoc.docId}/file/`); .replace(/\(file:\/\//g, `(./${this.pdoc.docId}/file/`);
// Get time and memory limit
try {
this.response.body.pdoc.config = await parseConfig(this.pdoc.config);
} catch (e) {
this.response.body.pdoc.config = `Cannot parse: ${e.message}`;
}
} }
@param('pid', Types.UnsignedInt) @param('pid', Types.UnsignedInt)
-1
View File
@@ -189,7 +189,6 @@ declare module './model/problem' {
data: FileInfo[], data: FileInfo[],
additional_file: FileInfo[], additional_file: FileInfo[],
hidden: boolean, hidden: boolean,
acMsg?: string,
html?: boolean, html?: boolean,
difficulty?: number, difficulty?: number,
/** @deprecated */ /** @deprecated */
+12 -10
View File
@@ -21,24 +21,26 @@ export async function parseConfig(config: string | ProblemConfig) {
let cfg: ProblemConfig = {}; let cfg: ProblemConfig = {};
if (typeof config === 'string') { if (typeof config === 'string') {
// TODO should validate here? // TODO should validate here?
cfg = readYamlCases(load(config) as Record<string, any>) as ProblemConfig; cfg = await readYamlCases(load(config) as Record<string, any>) as ProblemConfig;
} else cfg = config; } else cfg = config;
if (cfg.cases) { if (cfg.subtasks.length) {
for (const c of cfg.cases) {
result.memoryMax = Math.max(result.memoryMax, c.memory);
result.memoryMin = Math.min(result.memoryMin, c.memory);
result.timeMax = Math.max(result.timeMax, c.time);
result.timeMin = Math.min(result.timeMin, c.time);
result.count++;
}
} else if (cfg.subtasks) {
for (const subtask of cfg.subtasks) { for (const subtask of cfg.subtasks) {
result.memoryMax = Math.max(result.memoryMax, subtask.memory); result.memoryMax = Math.max(result.memoryMax, subtask.memory);
result.memoryMin = Math.min(result.memoryMin, subtask.memory); result.memoryMin = Math.min(result.memoryMin, subtask.memory);
result.timeMax = Math.max(result.timeMax, subtask.time); result.timeMax = Math.max(result.timeMax, subtask.time);
result.timeMin = Math.min(result.timeMin, subtask.time); result.timeMin = Math.min(result.timeMin, subtask.time);
} }
} else if (cfg.time || cfg.memory) {
if (cfg.time) result.timeMax = result.timeMin = cfg.time as unknown as number;
if (cfg.memory) result.memoryMax = result.memoryMin = cfg.memory as unknown as number;
} }
if (result.memoryMax < result.memoryMin) {
result.memoryMax = result.memoryMin = 256;
}
if (result.timeMax < result.timeMin) {
result.timeMax = result.timeMin = 1000;
}
return result;
} }
global.Hydro.lib.testdataConfig = { parseConfig }; global.Hydro.lib.testdataConfig = { parseConfig };
+1 -3
View File
@@ -28,8 +28,7 @@ export class ProblemModel {
static PROJECTION_PUBLIC: Field[] = [ static PROJECTION_PUBLIC: Field[] = [
...ProblemModel.PROJECTION_LIST, ...ProblemModel.PROJECTION_LIST,
'content', 'html', 'data', 'config', 'acMsg', 'content', 'html', 'data', 'config', 'additional_file',
'additional_file',
]; ];
static extend(getter: Getter) { static extend(getter: Getter) {
@@ -258,7 +257,6 @@ ProblemModel.extend((docId, pid) => ({
additional_file: [], additional_file: [],
hidden: true, hidden: true,
config: '', config: '',
acMsg: '',
difficulty: 0, difficulty: 0,
})); }));
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/migrate-vijos", "name": "@hydrooj/migrate-vijos",
"version": "1.4.0", "version": "1.4.1",
"main": "package.json", "main": "package.json",
"repository": "git@github.com:hydro-dev/Hydro.git", "repository": "git@github.com:hydro-dev/Hydro.git",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
-1
View File
@@ -74,7 +74,6 @@ const tasks = {
return res; return res;
}, },
}, },
ac_msg: 'acMsg',
parent_doc_id: { parent_doc_id: {
field: 'parentId', field: 'parentId',
processer: (parentId, doc) => { processer: (parentId, doc) => {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/ui-default", "name": "@hydrooj/ui-default",
"version": "4.5.14", "version": "4.5.15",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"main": "hydro.js", "main": "hydro.js",
@@ -126,10 +126,6 @@
{% else %} {% else %}
{{ _('(None)') }} {{ _('(None)') }}
{% endif %} {% endif %}
{% if pdoc['acMsg'] and pdoc['psdoc']['status'] == STATUS.STATUS_ACCEPTED %}
<dt>{{ _('AC Message') }}</dt>
<dd>{{ pdoc['acMsg'] }}</dd>
{% endif %}
</dd> </dd>
{% endif %} {% endif %}
{% endif %} {% endif %}
@@ -25,7 +25,27 @@
<div class="section"> <div class="section">
<div class="problem-content-container"><div class="problem-content" data-marker-enabled> <div class="problem-content-container"><div class="problem-content" data-marker-enabled>
<div class="section__header non-scratchpad--hide"> <div class="section__header non-scratchpad--hide">
<h1>{{ pdoc.title }}</h1> <h1 class="section__title">
{%- if rdoc -%}
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
<span class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
{{ rdoc.score }}
</span>
{%- endif %}
{{ pdoc.title }}
</h1>
</div>
<div class="section__header scratchpad--hide">
{% if rdoc %}
<h1 class="section__title">
<span class="icon record-status--icon {{ model.builtin.STATUS_CODES[rdoc['status']] }}"></span>
<span class="record-status--text {{ model.builtin.STATUS_CODES[rdoc['status']] }}">
{{ rdoc.score }}
</span>
</h1>
{% endif %}
<span class="record-status--text">{{ _('Time Limit') }}: {{ pdoc.config.timeMax if pdoc.config.timeMax == pdoc.config.timeMin else pdoc.config.timeMin+'~'+pdoc.config.timeMax }}ms</span>
<span class="record-status--text">{{ _('Memory Limit') }}: {{ pdoc.config.memoryMax if pdoc.config.memoryMax == pdoc.config.memoryMin else pdoc.config.memoryMin+'~'+pdoc.config.memoryMax }}MiB</span>
</div> </div>
<div class="section__body typo"> <div class="section__body typo">
{% if tdoc %} {% if tdoc %}
+7 -5
View File
@@ -35,8 +35,8 @@ export default async function readYamlCases(cfg: Record<string, any> = {}, check
} else if (cfg.cases) { } else if (cfg.cases) {
config.subtasks = [{ config.subtasks = [{
score: parseInt(cfg.score, 10) || Math.floor(100 / cfg.cases.length), score: parseInt(cfg.score, 10) || Math.floor(100 / cfg.cases.length),
time_limit_ms: parseTimeMS(cfg.time || '1s'), time: parseTimeMS(cfg.time || '1s'),
memory_limit_mb: parseMemoryMB(cfg.memory || '512m'), memory: parseMemoryMB(cfg.memory || '256m'),
cases: [], cases: [],
type: 'sum', type: 'sum',
}]; }];
@@ -63,11 +63,13 @@ export default async function readYamlCases(cfg: Record<string, any> = {}, check
score: parseInt(subtask.score, 10), score: parseInt(subtask.score, 10),
if: subtask.if || [], if: subtask.if || [],
cases, cases,
time_limit_ms: parseTimeMS(subtask.time || cfg.time || '1s'), time: parseTimeMS(subtask.time || cfg.time || '1s'),
memory_limit_mb: parseMemoryMB(subtask.memory || cfg.memory || '512m'), memory: parseMemoryMB(subtask.memory || cfg.memory || '256m'),
}); });
} }
} }
if (cfg.time) config.time = parseTimeMS(cfg.time);
if (cfg.memory) config.memory = parseMemoryMB(cfg.memory);
if (config.type === 'submit_answer' && !cfg.outputs) throw new Error('outputs config not found'); if (config.type === 'submit_answer' && !cfg.outputs) throw new Error('outputs config not found');
return Object.assign(cfg, config); return Object.assign(cfg, config);
} }
@@ -83,7 +85,7 @@ export function convertIniConfig(ini: string) {
cases: [{ input: `input/${input.toLowerCase()}`, output: `output/${output.toLowerCase()}` }], cases: [{ input: `input/${input.toLowerCase()}`, output: `output/${output.toLowerCase()}` }],
score: parseInt(score, 10), score: parseInt(score, 10),
time: `${time}s`, time: `${time}s`,
memory: '512m', memory: '256m',
}; };
if (!Number.isNaN(parseInt(memory, 10))) cur.memory = `${Math.floor(parseInt(memory, 10) / 1024)}m`; if (!Number.isNaN(parseInt(memory, 10))) cur.memory = `${Math.floor(parseInt(memory, 10) / 1024)}m`;
res.subtasks.push(cur); res.subtasks.push(cur);
+4 -2
View File
@@ -161,13 +161,15 @@ const TIME_UNITS = { '': 1000, m: 1, u: 0.001 };
const MEMORY_RE = /^([0-9]+(?:\.[0-9]*)?)([kmg])b?$/i; const MEMORY_RE = /^([0-9]+(?:\.[0-9]*)?)([kmg])b?$/i;
const MEMORY_UNITS = { k: 1 / 1024, m: 1, g: 1024 }; const MEMORY_UNITS = { k: 1 / 1024, m: 1, g: 1024 };
export function parseTimeMS(str: string) { export function parseTimeMS(str: string | number) {
if (typeof str === 'number') return str;
const match = TIME_RE.exec(str); const match = TIME_RE.exec(str);
if (!match) throw new Error(`${str} error parsing time`); if (!match) throw new Error(`${str} error parsing time`);
return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2]]); return Math.floor(parseFloat(match[1]) * TIME_UNITS[match[2]]);
} }
export function parseMemoryMB(str: string) { export function parseMemoryMB(str: string | number) {
if (typeof str === 'number') return str;
const match = MEMORY_RE.exec(str); const match = MEMORY_RE.exec(str);
if (!match) throw new Error(`${str} error parsing memory`); if (!match) throw new Error(`${str} error parsing memory`);
return Math.ceil(parseFloat(match[1]) * MEMORY_UNITS[match[2]]); return Math.ceil(parseFloat(match[1]) * MEMORY_UNITS[match[2]]);
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@hydrooj/utils", "name": "@hydrooj/utils",
"version": "1.0.1", "version": "1.0.2",
"description": "hydrooj utils", "description": "hydrooj utils",
"main": "package.json", "main": "package.json",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",