94 lines
2.8 KiB
Odin
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
|
||
|
|
}
|