Skip to content

Commit 2e9df10

Browse files
committed
WIP
1 parent 53c5765 commit 2e9df10

3 files changed

Lines changed: 140 additions & 2 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright (C) 2024 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
@file:JvmMultifileClass
17+
@file:JvmName("Okio")
18+
19+
package okio
20+
21+
import kotlin.jvm.JvmMultifileClass
22+
import kotlin.jvm.JvmName
23+
import kotlin.jvm.JvmOverloads
24+
25+
/**
26+
* Return a new [Source] whose [read function][Source.read] returns -1 after [byteCount]
27+
* bytes have been read.
28+
*
29+
* @param onReadExhausted Callback invoked once when the end of bytes has been reached. It receives
30+
* `true` if the end of bytes was because the underlying stream did not contain enough bytes and
31+
* `false` if [byteCount] bytes were successfully read.
32+
*/
33+
@JvmOverloads
34+
public fun Source.limit(
35+
byteCount: Long,
36+
onReadExhausted: (eof: Boolean) -> Unit = {},
37+
): Source {
38+
require(byteCount >= 0) { "byteCount < 0: $byteCount" }
39+
return FixedLengthSource(this, byteCount, onReadExhausted, truncate = true)
40+
}
41+
42+
internal class FixedLengthSource(
43+
delegate: Source,
44+
private var bytesRemaining: Long,
45+
onReadExhausted: (eof: Boolean) -> Unit,
46+
private val truncate: Boolean,
47+
) : ForwardingSource(delegate) {
48+
/** `null` once invoked. */
49+
private var onReadExhausted: ((eof: Boolean) -> Unit)? = onReadExhausted
50+
51+
override fun read(sink: Buffer, byteCount: Long): Long {
52+
val requestBytes = if (truncate) {
53+
if (bytesRemaining == 0L) {
54+
// If the limit was 0 we want to wait until the first call to this function before
55+
// triggering the callback.
56+
onReadExhausted?.invoke(false)
57+
onReadExhausted = null
58+
return -1L
59+
}
60+
minOf(bytesRemaining, byteCount)
61+
} else {
62+
byteCount
63+
}
64+
65+
val readBytes = super.read(sink, requestBytes)
66+
if (readBytes == -1L) {
67+
onReadExhausted!!(true)
68+
onReadExhausted = null
69+
return -1L
70+
}
71+
72+
bytesRemaining -= readBytes
73+
74+
if (bytesRemaining == 0L) {
75+
onReadExhausted!!(false)
76+
onReadExhausted = null
77+
}
78+
return readBytes
79+
}
80+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright (C) 2024 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package okio
17+
18+
import kotlin.test.Test
19+
import kotlin.test.assertEquals
20+
import kotlin.test.assertFails
21+
22+
class FixedLengthSourceTest2 {
23+
@Test fun byteCountNotNegative() {
24+
val source = Buffer()
25+
val t = assertFails {
26+
source.limit(-2)
27+
}
28+
assertEquals(IllegalArgumentException::class, t::class)
29+
assertEquals("byteCount < 0: -2", t.message)
30+
}
31+
32+
@Test fun zeroLimit() {
33+
val source = Buffer().writeUtf8("they're moving in herds")
34+
val destination = Buffer()
35+
36+
var callbacks = 0
37+
val limited = source.limit(0) { eof ->
38+
callbacks += if (eof) -1 else 1
39+
}
40+
41+
// We don't want the creation of the zero-length limit to synchronously trigger the callback
42+
// or read any data.
43+
assertEquals(0, callbacks)
44+
assertEquals(23, source.size)
45+
46+
// First read triggers callback, moves no data.
47+
assertEquals(-1, limited.read(destination, 10))
48+
assertEquals(1, callbacks)
49+
assertEquals(23, source.size)
50+
assertEquals(0, destination.size)
51+
52+
// Subsequent reads do not trigger callback, also move no data.
53+
assertEquals(-1, limited.read(destination, 10))
54+
assertEquals(1, callbacks)
55+
assertEquals(23, source.size)
56+
assertEquals(0, destination.size)
57+
}
58+
}

okio/src/zlibMain/kotlin/okio/ZipFileSystem.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ internal class ZipFileSystem internal constructor(
105105

106106
return when (entry.compressionMethod) {
107107
COMPRESSION_METHOD_STORED -> {
108-
FixedLengthSource(source, entry.size, truncate = true)
108+
source.limit(entry.size)
109109
}
110110
else -> {
111111
val inflaterSource = InflaterSource(
112-
FixedLengthSource(source, entry.compressedSize, truncate = true),
112+
source.limit(entry.compressedSize),
113113
Inflater(true),
114114
)
115115
FixedLengthSource(inflaterSource, entry.size, truncate = false)

0 commit comments

Comments
 (0)