package com.jaytux.simd.server
import com.jaytux.simd.data.*
import com.jaytux.simd.server.RouteCache.register
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.datetime.LocalDate
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import org.jetbrains.exposed.dao.UUIDEntity
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.like
import org.jetbrains.exposed.sql.transactions.transaction
import java.util.*
@Serializable
data class IntrinsicSummary(@Serializable(with = UUIDSerializer::class) val id: UUID, val name: String)
@Serializable
data class Param(val name: String, val type: String)
@Serializable
data class Instruction(val mnemonic: String, val xed: String, val form: String?)
@Serializable
data class PlatformPerformance(val platform: String, val latency: Float?, val throughput: Float?)
@Serializable
data class IntrinsicDetails(
@Serializable(with = UUIDSerializer::class) val id: UUID,
val name: String,
val returnType: String,
val returnVar: String?,
val description: String,
val operations: String?,
val category: String,
val cpuid: String?,
val tech: String,
val params: List,
val instructions: List?,
val performance: List?
)
@Serializable
data class Versioning(
val intelVersion: String, val intelUpdate: LocalDate, val scrapeDate: LocalDate
)
fun Routing.installGetAll() {
getPagedUrl("/all", "Get a quick overview of all endpoints", { 100 }, { IntrinsicSummary(it.id.value, it.mnemonic) }) {
Intrinsic.all().orderAsc(Intrinsics.mnemonic)
}
getPagedUrl("/cpuid", "Get a list of all CPUIDs", { 100 }, { it.name }) { CPUID.all().orderAsc(CPUIDs.name) }
getPagedUrl("/tech", "Get a list of all technologies", { 100 }, { it.name }) { Tech.all().orderAsc(Techs.name) }
getPagedUrl("/category", "Get a list of all intrinsic categories", { 100 }, { it.name }) { Category.all().orderAsc(Categories.name) }
getPagedUrl("/types", "Get a list of all used C/C++ types", { 100 }, { it.name }) { CppType.all().orderAsc(CppTypes.name) }
}
fun Routing.installSearch() {
getPagedRequest("/search", "Search for intrinsics matching certain filters", { 100 }, { IntrinsicSummary(it[Intrinsics.id].value, it[Intrinsics.mnemonic]) }) {
fun resolveAny(key: String, finder: (String) -> SizedIterable): List>? {
return call.request.queryParameters[key]?.let {
try {
val list = Json.decodeFromString>(it)
list.map {
finder(it).firstOrNull()?.id
?: throw HttpError("Unknown $key: ${list.joinToString(", ")}")
}
}
catch(e: HttpError) {
throw e
}
catch(e: Exception) {
throw HttpError("Malformed $key parameter: $it")
}
}
}
val name = call.request.queryParameters["name"]
val returnType = call.request.queryParameters["return"]?.let {
CppType.find { CppTypes.name eq it }.firstOrNull()
?: throw HttpError("Unknown return type: $it")
}
val anyCpuid = resolveAny("cpuid") { CPUID.find { CPUIDs.name eq it } }
val anyTech = resolveAny("tech") { Tech.find { Techs.name eq it } }
val anyCat = resolveAny("category") { Category.find { Categories.name eq it } }
val desc = call.request.queryParameters["desc"]
var results = Intrinsics.selectAll().where {
val build = listOf(
name?.let { Intrinsics.mnemonic like "%$it%" },
returnType?.let { Intrinsics.returnType eq it.id },
anyCpuid?.let { Intrinsics.cpuid inList it },
anyTech?.let { Intrinsics.tech inList it },
anyCat?.let { Intrinsics.category inList it },
desc?.let { Intrinsics.description like "%$it%" }
).filterNotNull()
build.fold(Op.TRUE as Op, { acc, op -> op and acc })
}
results.orderAsc(Intrinsics.mnemonic)
}
}
fun Routing.installDetails() {
register(HttpMethod.Get, "/details/{id}", "Get details of an intrinsic by its ID")
get("/details/{id}") {
runCatching {
transaction {
val id = call.parameters["id"]?.let {
try {
UUID.fromString(it)
}
catch(e: IllegalArgumentException) {
throw HttpError("Invalid UUID: $it")
}
} ?: throw HttpError("Missing or invalid ID")
val intrinsic = Intrinsic.findById(id)
?: throw HttpError("Unknown intrinsic: $id")
IntrinsicDetails(
id = intrinsic.id.value,
name = intrinsic.mnemonic,
returnType = intrinsic.returnType.name,
returnVar = intrinsic.returnVar,
description = intrinsic.description,
operations = intrinsic.operations,
category = intrinsic.category.name,
cpuid = intrinsic.cpuid?.name,
tech = intrinsic.tech.name,
params = intrinsic.arguments.orderAsc(IntrinsicArguments.index)
.map { Param(it.name, it.type.name) },
instructions = intrinsic.instructions.emptyToNull()
?.map { Instruction(it.mnemonic, it.xed, it.form) },
performance = intrinsic.instructions.firstOrNull()?.let {
(Performances innerJoin Platforms).selectAll().where {
Performances.instruction eq it.id
}.map {
PlatformPerformance(
platform = it[Platforms.name],
latency = it[Performances.latency],
throughput = it[Performances.throughput]
)
}
}
)
}
}
}
}
fun Routing.installVersion() {
register(HttpMethod.Get, "/version", "Get the current version of the data")
get("/version") {
val upd = Loader.DatedVersion.fromPrefs()
call.respond(Versioning(upd.version.toString(), intelUpdate = upd.releaseDate, upd.updateDate))
}
}