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)) } }