안녕하세요, 클라이언트의 요청 로그 포맷(count 바이트 하나, 그다음 그 개수만큼의 key/value 쌍, 각 쌍은 길이 바이트 하나와 바이트들)을 읽는 작은 디코더를 Rust로 짜고 있어요. 바이트는 소켓에서 제멋대로 된 청크로 들어와서, main이 Vec<u8>을 들고 있다가 청크가 올 때마다 붙이고, Incomplete가 나올 때까지 next_record를 부르고, 파서가 썼다고 하는 만큼 drain해요.
한 청크로 넣으면 레코드 세 개가 다 잘 찍혀요. 그런데 첫 번째 인자로 청크 크기를 7이나 10으로 주면 두 가지로 이상해져요. 하나는 레코드에 필드가 더 붙어서 나오는 거예요(끝나지 않은 레코드의 key가 다음 레코드 안에 나타나요). 다른 하나는 한 청크로 돌릴 때는 받아들여지는 바이트에서 malformed at offset N이 나오는 거고요. 두 증상이 같은 청크 크기에서 나오는 것 같지는 않아서, 버그가 하나인지 둘인지 모르겠어요.
계속 drain을 의심했는데, 고쳐 봐도 나오는 쓰레기 값만 달라져요. 루프 도는 동안 &buf를 들고 있는 것도 걸렸고요(컴파일은 되니까 아닐 것 같긴 해요). Incomplete는 "에러가 아니라 그냥 기다리면 됨"으로 취급하고 있고 루프도 그렇게 동작하는데, 뭐가 빠졌는지 모르겠어요.
fn take<'a>(&mut self, buf: &'a [u8], n: usize) -> Result<&'a [u8], ParseError> {
if self.pos + n > buf.len() {
return Err(ParseError::Incomplete);
}
let s = &buf[self.pos..self.pos + n];
self.pos += n;
Ok(s)
}
fn take_str(&mut self, buf: &[u8]) -> Result<String, ParseError> {
let len = self.take(buf, 1)?[0] as usize;
let raw = self.take(buf, len)?;
std::str::from_utf8(raw)
.map(|s| s.to_string())
.map_err(|_| ParseError::Malformed { offset: self.pos - len })
}
pub fn next_record(&mut self, buf: &[u8]) -> Result<Record, ParseError> {
let count = self.take(buf, 1)?[0] as usize;
if count == 0 || count > self.max_fields {
return Err(ParseError::Malformed { offset: self.pos - 1 });
}
for _ in 0..count {
let k = self.take_str(buf)?;
let v = self.take_str(buf)?;
self.scratch.push((k, v));
}
Ok(Record { fields: std::mem::take(&mut self.scratch) })
}
그리고 main의 루프예요(Parser는 pos, scratch: Vec<(String, String)>, max_fields를 들고 있어요).
for piece in data.chunks(chunk) {
buf.extend_from_slice(piece);
loop {
match parser.next_record(&buf) {
Ok(rec) => { seen += 1; println!("record {}: {:?}", seen, rec.fields); }
Err(ParseError::Incomplete) => break,
Err(ParseError::Malformed { offset }) => {
eprintln!("malformed at offset {}", offset);
return;
}
}
}
buf.drain(..parser.pos());
parser.reset_pos();
}
제가 뭘 놓치고 있을까요?