blob: 001bea2f8c08ae6edf6f34777f5541dea5eb4a60 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package org.meshbay.client.photos
import java.io.FilterInputStream
import java.io.InputStream
/**
* One `Range: bytes=a-b` of a file the page fetches a piece at a time, so a
* video of gigabytes never sits whole in the page's memory: the upload reads
* one chunk, sends it, and asks for the next.
*/
object ByteRange {
/** The first and last byte asked for, inside `size`; null for no range or one this does not serve. */
fun parse(header: String?, size: Long): LongRange? {
val m = Regex("^bytes=(\\d+)-(\\d*)$").matchEntire(header?.trim() ?: return null) ?: return null
val first = m.groupValues[1].toLongOrNull() ?: return null
val last = m.groupValues[2].takeIf { it.isNotEmpty() }?.toLongOrNull() ?: (size - 1)
if (first >= size || last < first) return null
return first..minOf(last, size - 1)
}
/** `stream` from `range.first`, ending after `range.last`. */
fun slice(stream: InputStream, range: LongRange): InputStream {
var toSkip = range.first
while (toSkip > 0) {
val n = stream.skip(toSkip)
if (n <= 0) { if (stream.read() < 0) break; toSkip -= 1 } else toSkip -= n
}
return object : FilterInputStream(stream) {
private var left = range.last - range.first + 1
override fun read(): Int {
if (left <= 0) return -1
return super.read().also { if (it >= 0) left -= 1 }
}
override fun read(b: ByteArray, off: Int, len: Int): Int {
if (left <= 0) return -1
return super.read(b, off, minOf(len.toLong(), left).toInt()).also { if (it > 0) left -= it }
}
}
}
}
|