Files
pdf-odin-llm/filter_runlength.odin
2026-09-25 23:32:29 +02:00

94 lines
2.8 KiB
Odin

package pdf
// RunLengthDecode: byte-oriented RLE, ISO 32000-2, 7.4.5.
//
// The stream is a sequence of length bytes:
//
// 0..127 the next length+1 bytes are literal
// 128 end of data
// 129..255 the next byte repeats 257-length times (2..128)
//
// Only ever seen on scanned images from older producers, and on those the data
// is as likely to be truncated as not, so running out of input mid-run ends
// the stream with what was decoded so far instead of failing.
// The filter expands by at most 128x, so a hostile stream can ask for a
// gigabyte from eight megabytes of input. Decoding stops at this point; a real
// image that needs more than this is not one we could raster anyway.
@(private = "file")
MAX_RUN_LENGTH_OUTPUT :: 1 << 30
// run_length_decode decodes RunLengthDecode data.
//
// A missing EOD marker, or input that ends inside a run, ends the stream
// rather than failing.
//
// The returned slice is owned by the caller and allocated with `allocator`.
run_length_decode :: proc(data: []byte, allocator := context.allocator) -> (out: []byte, err: Error) {
n := run_length_scan(data, nil)
if n == 0 {
return nil, .None
}
buf, alloc_err := make([]byte, n, allocator)
if alloc_err != nil {
return nil, .Out_Of_Memory
}
run_length_scan(data, buf)
return buf, .None
}
// run_length_scan measures the output when `out` is nil and fills it
// otherwise; see the note in filter_ascii.odin on why the work is done twice.
@(private)
run_length_scan :: proc(data: []byte, out: []byte) -> int {
n := 0
i := 0
// `i` strictly increases on every branch, so the loop is bounded by the
// input length without needing separate fuel.
for i < len(data) {
length := int(data[i])
i += 1
if length == 128 {
break
}
if length < 128 {
count := length + 1
// A literal run reaching past the end takes what is there; the
// stream is truncated, and the bytes before the cut are still good.
if count > len(data) - i {
count = len(data) - i
}
for k in 0 ..< count {
if n >= MAX_RUN_LENGTH_OUTPUT {
return n
}
if n < len(out) {
out[n] = data[i + k]
}
n += 1
}
i += count
if count < length + 1 {
break
}
continue
}
if i >= len(data) {
break
}
b := data[i]
i += 1
count := 257 - length
for _ in 0 ..< count {
if n >= MAX_RUN_LENGTH_OUTPUT {
return n
}
if n < len(out) {
out[n] = b
}
n += 1
}
}
return n
}