KStarbound/src/main/kotlin/ru/dbotthepony/kstarbound/Ext.kt
2023-10-22 00:45:22 +07:00

87 lines
2.8 KiB
Kotlin

package ru.dbotthepony.kstarbound
import com.google.common.collect.ImmutableMap
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import ru.dbotthepony.kstarbound.api.IStarboundFile
import ru.dbotthepony.kstarbound.util.KOptional
import java.util.Arrays
import java.util.concurrent.Callable
import java.util.concurrent.ForkJoinPool
import java.util.concurrent.ForkJoinTask
import java.util.stream.Stream
import kotlin.reflect.KProperty
inline fun <reified T> GsonBuilder.registerTypeAdapter(adapter: TypeAdapter<T>): GsonBuilder {
return registerTypeAdapter(T::class.java, adapter)
}
inline fun <reified T> GsonBuilder.registerTypeAdapter(noinline factory: (Gson) -> TypeAdapter<T>): GsonBuilder {
val token = TypeToken.get(T::class.java)
return registerTypeAdapterFactory(object : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type == token) {
return factory(gson) as TypeAdapter<T>
}
return null
}
})
}
fun <T> Array<T>.stream(): Stream<T> = Arrays.stream(this)
operator fun <T> ThreadLocal<T>.getValue(thisRef: Any, property: KProperty<*>): T {
return get()
}
operator fun <T> ThreadLocal<T>.setValue(thisRef: Any, property: KProperty<*>, value: T) {
set(value)
}
operator fun <K, V> ImmutableMap.Builder<K, V>.set(key: K, value: V): ImmutableMap.Builder<K, V> = put(key, value)
fun String.sintern(): String = Starbound.STRINGS.intern(this)
inline fun <reified T> Gson.fromJson(reader: JsonReader): T? = fromJson<T>(reader, T::class.java)
/**
* guarantees even distribution of tasks while also preserving encountered order of elements
*/
fun <T> Collection<IStarboundFile>.batch(executor: ForkJoinPool, batchSize: Int = 16, mapper: (IStarboundFile) -> KOptional<T>): Stream<T> {
require(batchSize >= 1) { "Invalid batch size: $batchSize" }
if (batchSize == 1 || size <= batchSize) {
val tasks = ArrayList<ForkJoinTask<KOptional<T>>>()
for (listedFile in this) {
tasks.add(executor.submit(Callable { mapper.invoke(listedFile) }))
}
return tasks.stream().map { it.join() }.filter { it.isPresent }.map { it.value }
}
val batches = ArrayList<ForkJoinTask<List<KOptional<T>>>>()
var batch = ArrayList<IStarboundFile>(batchSize)
for (listedFile in this) {
batch.add(listedFile)
if (batch.size >= batchSize) {
val mbatch = batch
batches.add(executor.submit(Callable { mbatch.map { mapper.invoke(it) } }))
batch = ArrayList(batchSize)
}
}
if (batch.isNotEmpty())
batches.add(executor.submit(Callable { batch.map { mapper.invoke(it) } }))
return batches.stream().flatMap { it.join().stream() }.filter { it.isPresent }.map { it.value }
}