Skip to content

Commit

Permalink
Merge pull request #77 from solrudev/develop
Browse files Browse the repository at this point in the history
0.7.3
  • Loading branch information
solrudev authored Oct 1, 2024
2 parents 8e9b4e6 + c5bc3b1 commit 688d2e0
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Ackpine depends on Jetpack libraries, so it's necessary to declare the `google()

```kotlin
dependencies {
val ackpineVersion = "0.7.2"
val ackpineVersion = "0.7.3"
implementation("ru.solrudev.ackpine:ackpine-core:$ackpineVersion")

// optional - Kotlin extensions and Coroutines support
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package ru.solrudev.ackpine.splits

import androidx.annotation.RestrictTo
import java.util.Collections
import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.RestrictsSuspension

/**
Expand All @@ -35,7 +37,7 @@ internal fun <T> closeableSequence(block: suspend CloseableSequenceScope<T>.() -

/**
* The scope for yielding values of a [CloseableSequence], provides [yield] and [yieldAll] suspension functions and
* [addCloseableResource] function to manage [AutoCloseable] resources.
* [use] function to manage [AutoCloseable] resources.
*
* @see closeableSequence
*/
Expand All @@ -49,9 +51,9 @@ internal interface CloseableSequenceScope<T> {
val isClosed: Boolean

/**
* Adds a [resource] to a set of [AutoCloseable] resources managed by the [CloseableSequence].
* Adds a resource to a set of [AutoCloseable] resources managed by the [CloseableSequence].
*/
fun addCloseableResource(resource: AutoCloseable)
fun <T : AutoCloseable> T.use(): T

/**
* Yields a value to the [Iterator] being built and suspends until the next value is requested.
Expand Down Expand Up @@ -81,7 +83,9 @@ private class CloseableSequenceImpl<T>(
@Volatile
private lateinit var scope: SequenceScope<T>

private val resources = mutableSetOf<AutoCloseable>()
private val resources = Collections.newSetFromMap(
ConcurrentHashMap<AutoCloseable, Boolean>()
)

override fun iterator(): Iterator<T> {
if (isConsumed) {
Expand All @@ -90,7 +94,9 @@ private class CloseableSequenceImpl<T>(
isConsumed = true
return iterator {
scope = this
block()
use {
block()
}
}
}

Expand All @@ -102,8 +108,9 @@ private class CloseableSequenceImpl<T>(
resources.clear()
}

override fun addCloseableResource(resource: AutoCloseable) {
resources += resource
override fun <T : AutoCloseable> T.use(): T {
resources += this
return this
}

override suspend fun yield(value: T) = scope.yield(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,7 @@ public object ZippedApkSplits {
*/
@JvmStatic
public fun getApksForFile(file: File): Sequence<Apk> = closeableSequence {
ZipFile(file).use { zipFile ->
addCloseableResource(zipFile)
zipFile.entries()
.asSequence()
.filterNot { isClosed }
.mapNotNull { zipEntry ->
zipFile.getInputStream(zipEntry).use { entryStream ->
Apk.fromZipEntry(file.absolutePath, zipEntry, entryStream)
}
}
.forEach { yield(it) }
}
yieldAllUsingFile(file)
}

/**
Expand All @@ -71,7 +60,7 @@ public object ZippedApkSplits {
return closeableSequence {
val file = uri.toFile(applicationContext)
if (file.canRead()) {
yieldAll(getApksForFile(file))
yieldAllUsingFile(file)
return@closeableSequence
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Expand All @@ -87,40 +76,47 @@ public object ZippedApkSplits {
}
}

@Suppress("BlockingMethodInNonBlockingContext")
private suspend inline fun CloseableSequenceScope<Apk>.yieldAllUsingFile(file: File) {
val zipFile = ZipFile(file).use()
zipFile.entries()
.asSequence()
.filterNot { isClosed }
.mapNotNull { zipEntry ->
zipFile.getInputStream(zipEntry).use { entryStream ->
// java.util.zip.ZipFile closes all entry streams when closed, no need to apply .use()
Apk.fromZipEntry(file.absolutePath, zipEntry, entryStream)
}
}
.forEach { yield(it) }
}

@RequiresApi(Build.VERSION_CODES.O)
private suspend inline fun CloseableSequenceScope<Apk>.yieldAllUsingFileChannel(context: Context, uri: Uri) {
context.contentResolver.openFileDescriptor(uri, "r").use { fd ->
fd ?: throw NullPointerException("ParcelFileDescriptor was null: $uri")
addCloseableResource(fd)
FileInputStream(fd.fileDescriptor).use { fileInputStream ->
addCloseableResource(fileInputStream)
org.apache.commons.compress.archivers.zip.ZipFile.builder()
.setSeekableByteChannel(fileInputStream.channel)
.get()
.use { zipFile ->
addCloseableResource(zipFile)
zipFile.entries
.asSequence()
.filterNot { isClosed }
.mapNotNull { zipEntry ->
zipFile.getInputStream(zipEntry).use { entryStream ->
addCloseableResource(entryStream)
Apk.fromZipEntry(uri.toString(), zipEntry, entryStream)
}
}
.forEach { yield(it) }
}
val fd = context.contentResolver.openFileDescriptor(uri, "r")?.use()
?: throw NullPointerException("ParcelFileDescriptor was null: $uri")
val fileInputStream = FileInputStream(fd.fileDescriptor).use()
val zipFile = org.apache.commons.compress.archivers.zip.ZipFile.builder()
.setSeekableByteChannel(fileInputStream.channel)
.get()
.use()
zipFile.entries
.asSequence()
.filterNot { isClosed }
.mapNotNull { zipEntry ->
zipFile.getInputStream(zipEntry).use { entryStream ->
entryStream.use()
Apk.fromZipEntry(uri.toString(), zipEntry, entryStream)
}
}
}
.forEach { yield(it) }
}

private suspend inline fun CloseableSequenceScope<Apk>.yieldAllUsingZipInputStream(context: Context, uri: Uri) {
ZipInputStream(context.contentResolver.openInputStream(uri)).use { zipStream ->
addCloseableResource(zipStream)
zipStream.entries()
.filterNot { isClosed }
.mapNotNull { zipEntry -> Apk.fromZipEntry(uri.toString(), zipEntry, zipStream) }
.forEach { yield(it) }
}
val zipStream = ZipInputStream(context.contentResolver.openInputStream(uri)).use()
zipStream.entries()
.filterNot { isClosed }
.mapNotNull { zipEntry -> Apk.fromZipEntry(uri.toString(), zipEntry, zipStream) }
.forEach { yield(it) }
}
}
7 changes: 7 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
Change Log
==========

Version 0.7.3 (2024-10-01)
--------------------------

### Bug fixes and improvements

- Fix resources not closing when throwing if `throwOnInvalidSplitPackage()` is applied and `ZippedApkSplits.getApksForUri()` delegates to `ZippedApkSplits.getApksForFile()`.

Version 0.7.2 (2024-09-30)
--------------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Ackpine depends on Jetpack libraries, so it's necessary to declare the `google()

```kotlin
dependencies {
val ackpineVersion = "0.7.2"
val ackpineVersion = "0.7.3"
implementation("ru.solrudev.ackpine:ackpine-core:$ackpineVersion")

// optional - Kotlin extensions and Coroutines support
Expand Down
2 changes: 1 addition & 1 deletion version.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
MAJOR_VERSION=0
MINOR_VERSION=7
PATCH_VERSION=2
PATCH_VERSION=3
SUFFIX=
SNAPSHOT=false

0 comments on commit 688d2e0

Please sign in to comment.