Skip to content

Commit 914e033

Browse files
committed
Add scroll-to-UB to rendering
1 parent d2909b5 commit 914e033

7 files changed

Lines changed: 194 additions & 191 deletions

File tree

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
#![no_main]
22

33
use libfuzzer_sys::fuzz_target;
4+
use std::io::Write;
5+
use ansi_to_html::Renderer;
46

57
fuzz_target!(|data: &[u8]| {
6-
let _ = ansi_to_html::render(String::new(), data);
8+
let mut renderer = Renderer::new(data);
9+
while let Some(line) = renderer.next_line().unwrap() {
10+
std::io::sink().write_all(&line).unwrap();
11+
}
12+
assert!(matches!(renderer.next_line(), Ok(None)));
713
});

ansi-to-html/src/lib.rs

Lines changed: 73 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,109 @@
1-
use std::collections::VecDeque;
21
use std::io::Read;
32

43
mod ansi;
54
mod perform;
65
mod renderer;
76

8-
pub struct Handle<R> {
9-
renderer: renderer::Renderer,
7+
pub struct Renderer<R> {
8+
inner: renderer::Renderer,
109
parser: vte::Parser,
1110
bytes: R,
12-
finished_line: Option<VecDeque<u8>>,
13-
wrote_final_line: bool,
11+
state: State,
12+
title: String,
1413
}
1514

16-
const FIRST_LINE: &str = r#"<!DOCTYPE html><html><body style="background:#111;color:#eee;"><body>
17-
<pre style="word-wrap:break-word;white-space:pre-wrap;font-size:14px;font-size-adjust:none;text-size-adjust:none;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;"></style><span>"#;
15+
#[rustfmt::skip]
16+
macro_rules! log_format {
17+
() => {
18+
r#"<html><head><style>
19+
body {{
20+
background: #111;
21+
color: #eee;
22+
}}
23+
pre {{
24+
word-wrap: break-word;
25+
white-space: pre-wrap;
26+
font-size: 14px;
27+
font-size-adjust: none;
28+
text-size-adjust: none;
29+
-webkit-text-size-adjust: 100%;
30+
-moz-text-size-adjust: 100%;
31+
-ms-text-size-adjust: 100%;
32+
}}
33+
</style><title>{}</title></head>
34+
<script>
35+
function scroll_to_ub() {{
36+
var ub = document.getElementById("ub");
37+
if (ub !== null) {{
38+
ub.scrollIntoView();
39+
}}
40+
}}
41+
</script>
42+
<body onload="scroll_to_ub()">
43+
<pre style="text-align: center;">{}</pre>
44+
<pre>"#
45+
}
46+
}
47+
48+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49+
enum State {
50+
Fresh,
51+
Rendering,
52+
Done,
53+
}
1854

19-
impl<R: Read> Handle<R> {
20-
pub fn new(bytes: R) -> Self {
55+
impl<R: Read> Renderer<R> {
56+
pub fn new(bytes: R, title: String) -> Self {
2157
Self {
22-
renderer: renderer::Renderer::new(String::new()),
58+
inner: renderer::Renderer::new(String::new()),
2359
parser: vte::Parser::new(),
2460
bytes,
25-
finished_line: Some(FIRST_LINE.as_bytes().to_vec().into()),
26-
wrote_final_line: false,
61+
state: State::Fresh,
62+
title,
2763
}
2864
}
2965

3066
pub fn is_empty(&self) -> bool {
31-
self.wrote_final_line && self.finished_line.is_none()
67+
self.state == State::Done
68+
}
69+
70+
fn first_line(&self) -> Vec<u8> {
71+
format!(log_format!(), self.title, self.title).into_bytes()
3272
}
33-
}
3473

35-
impl<R> Read for Handle<R>
36-
where
37-
R: Read,
38-
{
39-
fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
40-
if buf.is_empty() {
41-
return Ok(0);
74+
pub fn next_line(&mut self) -> Result<Option<Vec<u8>>, std::io::Error> {
75+
match self.state {
76+
State::Done => return Ok(None),
77+
State::Fresh => {
78+
self.state = State::Rendering;
79+
return Ok(Some(self.first_line()));
80+
}
81+
State::Rendering => {}
4282
}
43-
while self.finished_line.is_none() && !self.wrote_final_line {
83+
loop {
4484
let mut byte = [0u8];
4585
let n = self.bytes.read(&mut byte)?;
4686
if n == 0 {
47-
if let Some(line) = self.renderer.remove_oldest_row() {
48-
self.finished_line = Some(line.into());
87+
let line = if let Some(line) = self.inner.remove_oldest_row() {
88+
Some(line)
4989
} else {
90+
self.state = State::Done;
5091
let mut line = Vec::new();
5192
line.extend(b"</span></pre></body><style>");
52-
self.renderer.emit_css(&mut line)?;
93+
self.inner.emit_css(&mut line)?;
5394
line.extend(b"</style></html>");
54-
self.finished_line = Some(line.into());
55-
self.wrote_final_line = true;
56-
}
57-
break;
95+
line.into()
96+
};
97+
return Ok(line);
5898
}
59-
self.parser.advance(&mut self.renderer, byte[0]);
60-
if let Some(mut line) = self.renderer.pop_completed_row() {
99+
self.parser.advance(&mut self.inner, byte[0]);
100+
if let Some(mut line) = self.inner.pop_completed_row() {
61101
if line.is_empty() {
62102
log::warn!("renderer produced an empty line!");
63-
line = vec![b'\n'];
103+
line = b"\n".to_vec();
64104
}
65-
self.finished_line = Some(line.into());
105+
return Ok(Some(line));
66106
}
67107
}
68-
let Some(line) = self.finished_line.as_mut() else {
69-
return Ok(0);
70-
};
71-
let n = buf.len().min(line.len());
72-
for (src, dst) in line.drain(..n).zip(buf.iter_mut().take(n)) {
73-
*dst = src;
74-
}
75-
if line.is_empty() {
76-
self.finished_line = None;
77-
}
78-
Ok(n)
79108
}
80109
}
81-
82-
pub fn render(name: String, bytes: &[u8]) -> (String, String) {
83-
let mut h = Handle {
84-
renderer: renderer::Renderer::new(name),
85-
parser: vte::Parser::new(),
86-
bytes,
87-
finished_line: None,
88-
wrote_final_line: false,
89-
};
90-
let mut html = Vec::new();
91-
h.read_to_end(&mut html).unwrap();
92-
(String::new(), String::from_utf8(html).unwrap())
93-
}

ansi-to-html/src/main.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use std::io::{copy, stdin, stdout};
1+
use std::io::{stdin, stdout, Write};
22

3-
fn main() {
3+
fn main() -> Result<(), std::io::Error> {
44
env_logger::init();
5-
let lock = stdin().lock();
6-
let mut handle = ansi_to_html::Handle::new(lock);
7-
copy(&mut handle, &mut stdout().lock()).unwrap();
5+
let mut renderer = ansi_to_html::Renderer::new(stdin().lock(), String::from("stdin"));
6+
let mut out = stdout().lock();
7+
while let Some(line) = renderer.next_line()? {
8+
out.write_all(&line)?;
9+
}
10+
Ok(())
811
}

server/Cargo.lock

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ log = "0.4.21"
1515
env_logger = "0.11.2"
1616
semver = "1.0.22"
1717
xz2 = "0.1.7"
18+
flate2 = "1.0.28"
1819

1920
[profile.release]
2021
debug = 1

0 commit comments

Comments
 (0)