<commit_before>/*
 * Copyright LWJGL. All rights reserved.
 * License terms: https://www.lwjgl.org/license
 */
package org.lwjgl.generator

import java.io.*

private const val STRUCT = "struct"
private const val ANONYMOUS = "*" // very easy to introduce bugs, unless this is an invalid character in Java identifiers

open class StructMember(
    val nativeType: NativeType,
    val name: String,
    val documentation: String
) : ModifierTarget<StructMemberModifier>() {

    init {
        if (nativeType is PointerType && nativeType.elementType is StructType)
            nativeType.elementType.definition.hasUsageInput()
    }

    override fun validate(modifier: StructMemberModifier) = modifier.validate(this)

    internal val offsetField
        get() = name.toUpperCase()

    internal fun offsetField(parentField: String): String {
        return if (parentField.isEmpty())
            offsetField
        else
            "${parentField}_$offsetField"
    }

    internal fun field(parentMember: String) = if (parentMember.isEmpty())
        if (name == "class") "$name\$" else name // TODO: use a list of Java keywords here
    else
        "${parentMember}_$name"

    /** hidden if false, contributes to layout only */
    var public = true
    /** mutable if true, even if the parent struct is not mutable */
    var mutable = false

    internal var links: String = ""
    internal var linkMode: LinkMode = LinkMode.SINGLE

    fun links(links: String, linkMode: LinkMode = LinkMode.SINGLE) {
        this.links = links
        this.linkMode = linkMode
    }
}

internal class StructMemberBuffer(
    nativeType: PointerType,
    name: String,
    documentation: String
) : StructMember(nativeType, name, documentation)

private open class StructMemberArray(
    nativeType: NativeType,
    name: String,
    documentation: String,
    /** Number of elements in the array. */
    val size: String,
    /** Number of pointer elements that must not be null. */
    val validSize: String
) : StructMember(nativeType, name, documentation) {

    val primitiveMapping get() = this.nativeType.let {
        if (it is PointerType) PrimitiveMapping.POINTER else it.mapping as PrimitiveMapping
    }

}

private class StructMemberCharArray(
    nativeType: CharType,
    name: String,
    documentation: String,
    size: String
) : StructMemberArray(nativeType, name, documentation, size, size)

private class StructMemberPadding(size: String, val condition: String?) : StructMemberArray(char, ANONYMOUS, "", size, size) {
    init {
        public = false
    }
}

private enum class MultiSetterMode {
    NORMAL,
    ALTER
}

private enum class AccessMode(val indent: String) {
    INSTANCE(t),
    FLYWEIGHT("$t$t");
}

class Struct(
    packageName: String,
    className: String,
    nativeSubPath: String = "",
    /** The native struct name. May be different than className. */
    val nativeName: String,
    private val union: Boolean,
    /** when true, a declaration is missing, we need to output one. */
    private val virtual: Boolean,
    /** when false, setters methods will not be generated. */
    private val mutable: Boolean,
    /** if specified, is the super-type of this struct. */
    private val extends: Struct?,
    /** when true, the struct layout will be built using native code. */
    private val nativeLayout: Boolean
) : GeneratorTargetNative(packageName, className, nativeSubPath) {

    companion object {
        private val bufferMethodMap = mapOf(
            "boolean" to "Byte",

            "byte" to "Byte",
            "char" to "Char",
            "short" to "Short",
            "int" to "Int",
            "long" to "Long",

            "float" to "Float",
            "double" to "Double"
        )

        private const val BUFFER_CAPACITY_PARAM = "capacity"
    }

    val nativeType get() = StructType(this)

    /* Output parameter or function result by value */
    private var usageOutput = false

    fun hasUsageOutput() {
        usageOutput = true
    }

    /* Input parameter */
    private var usageInput = false

    fun hasUsageInput() {
        usageInput = true
    }

    /* Function result by reference */
    private var usageResultPointer = false

    fun hasUsageResultPointer() {
        usageResultPointer = true
    }

    private var static: String? = null
    fun static(expression: String) {
        static = expression
    }

    private val members = ArrayList<StructMember>()

    private val visibleMembers
        get() = members.asSequence().filter { it !is StructMemberPadding }

    private val publicMembers
        get() = members.asSequence().filter { it.public }

    private fun mutableMembers(members: Sequence<StructMember> = publicMembers): Sequence<StructMember> = members.let {
        if (mutable) it else it.filter { member -> member.mutable }
    }

    private val settableMembers: Sequence<StructMember> by lazy(LazyThreadSafetyMode.NONE) {
        val mutableMembers = mutableMembers()
        mutableMembers.filter { !it.has<AutoSizeMember>() || it.get<AutoSizeMember>().keepSetter(mutableMembers) }
    }

    private fun hasMutableMembers(members: Sequence<StructMember> = publicMembers) = this.members.isNotEmpty() && (mutable || mutableMembers(members).any())

    operator fun get(name: String): StructMember = members.asSequence().first { it.name == name }

    fun StructMember.replace(old: StructMember) {
        members.remove(this)
        members[members.indexOf(old)] = this
    }

    private fun add(member: StructMember): StructMember {
        members.add(member)
        return member
    }

    // Plain field
    fun NativeType.member(name: String, documentation: String) = add(StructMember(this, name, documentation))

    // Points to an array of structs, not a single struct
    fun PointerType.buffer(name: String, documentation: String): StructMember {
        if (this.elementType !is StructType)
            throw IllegalArgumentException("Only pointer-to-struct types can be used as buffer struct members")

        return add(StructMemberBuffer(this, name, documentation))
    }

    // Array field
    fun NativeType.array(name: String, documentation: String, size: Int, validSize: Int = size)
        = array(name, documentation, size.toString(), validSize.toString())

    fun NativeType.array(name: String, documentation: String, size: String, validSize: String = size)
        = add(StructMemberArray(this, name, documentation, size, validSize))

    // CharSequence special-case
    fun CharType.array(name: String, documentation: String, size: Int) = array(name, documentation, size.toString())

    fun CharType.array(name: String, documentation: String, size: String) = add(StructMemberCharArray(this, name, documentation, size))

    fun padding(size: Int, condition: String? = null) = padding(size.toString(), condition)
    fun padding(size: String, condition: String? = null) = add(StructMemberPadding(size, condition))

    /** Anonymous nested member struct definition. */
    fun struct(init: Struct.() -> Unit): StructMember = struct(ANONYMOUS, ANONYMOUS, init)

    /** Nested member struct definition. */
    fun struct(name: String, documentation: String, init: Struct.() -> Unit): StructMember {
        val struct = Struct(ANONYMOUS, ANONYMOUS, "", ANONYMOUS, false, false, mutable, null, false)
        struct.init()
        return StructType(struct).member(name, documentation)
    }

    /** Anonymous nested member union definition. */
    fun union(init: Struct.() -> Unit): StructMember = union(ANONYMOUS, ANONYMOUS, init)

    /** Nested member union definition. */
    fun union(name: String, documentation: String, init: Struct.() -> Unit): StructMember {
        val struct = Struct(ANONYMOUS, ANONYMOUS, "", ANONYMOUS, true, false, mutable, null, false)
        struct.init()
        return StructType(struct).member(name, documentation)
    }

    /** The nested struct's members are embedded in the parent struct. */
    private val StructMember.isNestedStruct
        get() = nativeType is StructType && this !is StructMemberArray

    /** The nested struct is not defined elsewhere, it's part of the parent struct's definition */
    private val StructMember.isNestedStructDefinition
        get() = isNestedStruct && (nativeType as StructType).name === ANONYMOUS

    private val StructMember.nestedMembers
        get() = (nativeType as StructType).definition.visibleMembers

    private val containsUnion: Boolean get() = union || members.any {
        it.isNestedStruct && (it.nativeType as StructType).let { it.name === ANONYMOUS && it.definition.containsUnion }
    }

    internal val validations: Sequence<String> by lazy(LazyThreadSafetyMode.NONE) {
        if (union)
            return@lazy emptySequence<String>()

        val validations = ArrayList<String>()

        fun MutableList<String>.addPointer(m: StructMember) = this.add("$t${t}long ${m.name} = memGetAddress($STRUCT + $className.${m.offsetField});")
        fun MutableList<String>.addCount(m: StructMember) = this.add("$t$t${m.nativeType.javaMethodType} ${m.name} = n${m.name}($STRUCT);")

        fun validate(m: StructMember, indent: String, hasPointer: Boolean = false): String {
            return if (m.nativeType.hasStructValidation) {
                if (m is StructMemberArray) {
                    """${
                    if (hasPointer) "" else "${indent}long ${m.name} = $STRUCT + $className.${m.offsetField};\n"
                    }${indent}for (int i = 0; i < ${getReferenceMember<AutoSizeMember>(m.name)?.name ?: m.size}; i++) {${
                    if (m is PointerType) {
                        if (m.validSize == m.size)
                            "\n$indent   check(memGetAddress(${m.name}));"
                        else
                            """
$indent   if (i < ${m.validSize}) {
$indent       check(memGetAddress(${m.name}));
$indent   } else if (memGetAddress(${m.name}) == NULL) {
$indent       break;
$indent   }"""
                    } else ""}
$indent   ${m.nativeType.javaMethodType}.validate(${m.name});
$indent   ${m.name} += POINTER_SIZE;
$indent}"""
                } else {
                    "${if (hasPointer) "" else
                        "${indent}long ${m.name} = memGetAddress($STRUCT + $className.${m.offsetField});\n" +
                        "${indent}check(${m.name});\n"
                    }$indent${m.nativeType.javaMethodType}.validate(${m.name}${getReferenceMember<AutoSizeMember>(m.name).let { if (it != null) ", ${it.name}" else "" }});"
                }
            } else
                "${indent}check(memGetAddress($STRUCT + $className.${m.offsetField}));"
        }

        fun validationBlock(condition: String, validations: String): String = "$t${t}if ($condition) {\n$validations\n$t$t}"

        mutableMembers().forEach { m ->
            if (m.has<AutoSizeMember>()) {
                m.get<AutoSizeMember>().let { autoSize ->
                    val refs = autoSize.members(mutableMembers())

                    if (autoSize.atLeastOne) {
                        // TODO: There will be redundancy here when one of the refs is a validateable struct array. But we don't have a case for it yet.
                        validations.addCount(m)

                        // if m != 0, make sure one of the auto-sized members is not null
                        validations.add(
                            "$t${t}if (${if (autoSize.optional) "\n$t$t$t${m.name} != 0 &&" else "\n$t$t$t${m.name} == 0 || ("}${
                            refs.map { "\n$t$t${t}memGetAddress($STRUCT + $className.${it.offsetField}) == NULL" }.joinToString(" &&")
                            }\n$t$t${if (autoSize.optional) "" else ")"}) {\n$t$t${t}throw new NullPointerException(\"At least one of ${refs.map { it.name }.joinToString()} must not be null\");\n$t$t}"
                        )
                    } else if (autoSize.optional) {
                        val refValidations = refs.filter { !it.has(nullable) }.map { ref ->
                            validate(ref, "$t$t$t")
                        }.joinToString("\n")

                        if (refValidations.isEmpty())
                            return@let

                        // if m != 0, make sure auto-sized members are not null
                        validations.add(
                            validationBlock("${if (refValidations.contains(", ${m.name})")) {
                                validations.addCount(m)
                                m.name
                            } else "n${m.name}($STRUCT)"} != 0", refValidations)
                        )
                    } else if (refs.any { it.nativeType.hasStructValidation })
                        validations.addCount(m)
                }
            } else if (m.nativeType is StructType && m.nativeType.definition.validations.any()) {
                validations.add(
                    if (m is StructMemberArray)
                        validate(m, "$t$t")
                    else
                        "$t$t${m.nativeType.javaMethodType}.validate($STRUCT + $className.${m.offsetField});"
                )
            } else if (m.nativeType is PointerType && getReferenceMember<AutoSizeMember>(m.name)?.get<AutoSizeMember>().let { it == null || !it.optional }) {
                if (m.nativeType.hasStructValidation) {
                    validations.add(
                        if (m.has(nullable)) {
                            validations.addPointer(m)
                            validationBlock("${m.name} != NULL", validate(m, "$t$t$t", hasPointer = true))
                        } else
                            validate(m, "$t$t")
                    )
                } else if (!m.has(nullable) && m.nativeType !is StructType) {
                    validations.add(validate(m, "$t$t"))
                }
            }
        }

        validations.asSequence()
    }

    /** Returns a member that has the specified ReferenceModifier with the specified reference. Returns null if no such parameter exists. */
    private inline fun <reified T> getReferenceMember(reference: String) where T : StructMemberModifier, T : ReferenceModifier = members.firstOrNull {
        it.has<T>(reference)
    } // Assumes at most 1 parameter will be found that references the specified parameter

    private fun PrintWriter.printDocumentation() {
        val builder = StringBuilder()

        val members = members.filter { it !is StructMemberPadding }
        if (documentation != null) {
            builder.append(documentation)
            if (members.isNotEmpty())
                builder.append("\n\n")
        }

        if (members.isNotEmpty()) {
            val memberDoc = printMemberDocumentation()
            if (memberDoc.isNotEmpty())
                builder
                    .append("<h3>Member documentation</h3>\n\n")
                    .append(ul(*memberDoc.toTypedArray()))
                    .append("\n\n")

            builder.append("<h3>Layout</h3>\n\n")
            builder.append(codeBlock(printStructLayout()))
        }

        if (builder.isNotEmpty())
            println(processDocumentation(builder.toString()).toJavaDoc(indentation = ""))
    }

    private val nativeNameQualified get() = (if (union) "union " else "struct ").let { type ->
        when {
            nativeName.startsWith(type) -> nativeName
            nativeName === ANONYMOUS    -> type.substring(0, type.length - 1)
            else                        -> "$type$nativeName"
        }
    }

    private fun Struct.printStructLayout(indentation: String = ""): String {
        val memberIndentation = "$indentation    "
        return """$nativeNameQualified {
${members.joinToString("\n$memberIndentation", prefix = memberIndentation) {
            if (it.isNestedStructDefinition)
                "${(it.nativeType as StructType).definition.printStructLayout(memberIndentation)}${if (it.name === ANONYMOUS) "" else " ${it.name}"};"
            else {
                val nativeType = it.nativeType.let {
                    (
                        if (it is PointerType && !it.includesPointer)
                            "${it.name}${if (!it.name.endsWith('*')) " " else ""}*"
                        else
                            it.name
                    ).let { nativeName ->
                        var elementType = it
                        while (true) {
                            if (elementType !is PointerType || elementType.elementType == null)
                                break

                            elementType = elementType.elementType!!
                        }

                        if (elementType is StructType || elementType is CallbackType)
                            nativeName.replace(elementType.name, "{@link ${elementType.javaMethodType} ${elementType.name}}")
                        else
                            nativeName
                    }
                }
                "${if (it has ConstMember) "const " else ""}$nativeType${if (it.name === ANONYMOUS) "" else " ${it.name}"}${if (it is StructMemberArray) "[${it.size}]" else ""};"
            }
        }}
$indentation}"""
    }

    private fun Struct.printMemberDocumentation(prefix: String = "", documentation: MutableList<String> = ArrayList()): List<String> {
        members.forEach {
            if (it.isNestedStructDefinition)
                (it.nativeType as StructType).definition.printMemberDocumentation(if (it.name === ANONYMOUS) prefix else "$prefix${it.name}.", documentation)
            else if (it.documentation.isNotEmpty() || it.links.isNotEmpty()) {
                val doc = if (it.links.isEmpty())
                    it.documentation
                else
                    it.linkMode.appendLinks(
                        it.documentation,
                        if (!it.links.contains('+')) it.links else linksFromRegex(it.links)
                    )
                documentation.add("{@code $prefix${it.name}} &ndash; $doc")
            }
        }
        return documentation
    }

    private fun StructMember.error(msg: String) {
        throw IllegalArgumentException("$msg [${this@Struct.className}, member: $name]")
    }

    private fun validate() {
        members.filter { it.has<AutoSizeMember>() }.forEach {
            val autoSize = it.get<AutoSizeMember>()

            autoSize.references.forEach { reference ->
                val bufferParam = members.firstOrNull { it.name == reference }
                if (bufferParam == null)
                    it.error("Reference does not exist: AutoSize($reference)")
                else {
                    if (bufferParam !is StructMemberArray) {
                        if (bufferParam.nativeType !is PointerType)
                            it.error("Reference must be a pointer type: AutoSize($reference)")

                        if (!bufferParam.nativeType.isPointerData)
                            it.error("Reference must not be a opaque pointer: AutoSize($reference)")

                        if ((bufferParam.nativeType as PointerType).elementType is StructType && bufferParam !is StructMemberBuffer)
                            it.error("Reference must be a struct buffer: AutoSize($reference)")
                    } else if (autoSize.optional || autoSize.atLeastOne)
                        it.error("Optional cannot be used with array references: AutoSize($reference)")

                    if (autoSize.atLeastOne && !bufferParam.has(nullable))
                        it.error("The \"atLeastOne\" option requires references to be nullable: AutoSize($reference)")
                }
            }
        }
    }

    override fun PrintWriter.generateJava() {
        validate()

        val nativeLayout = this@Struct.nativeLayout || members.isEmpty()
        if (!nativeLayout && preamble.hasNativeDirectives)
            kotlin.io.println("${t}Unnecessary native directives in struct: $packageName.$className")

        print(HEADER)
        println("package $packageName;\n")

        println("import java.nio.*;\n")

        val mallocable = mutable || usageOutput || (usageInput && !usageResultPointer)

        if (mallocable || members.any { m -> m.nativeType.let { it is PointerType && it.mapping === PointerMapping.DATA_POINTER && (it.elementType !is StructType || m is StructMemberArray) } })
            println("import org.lwjgl.*;")
        println("import org.lwjgl.system.*;\n")
<commit_msg>fix(generator): ignore struct input usages in structs without allocation methods<commit_after>/*
 * Copyright LWJGL. All rights reserved.
 * License terms: https://www.lwjgl.org/license
 */
package org.lwjgl.generator

import java.io.*

private const val STRUCT = "struct"
private const val ANONYMOUS = "*" // very easy to introduce bugs, unless this is an invalid character in Java identifiers

open class StructMember(
    val nativeType: NativeType,
    val name: String,
    val documentation: String
) : ModifierTarget<StructMemberModifier>() {

    override fun validate(modifier: StructMemberModifier) = modifier.validate(this)

    internal val offsetField
        get() = name.toUpperCase()

    internal fun offsetField(parentField: String): String {
        return if (parentField.isEmpty())
            offsetField
        else
            "${parentField}_$offsetField"
    }

    internal fun field(parentMember: String) = if (parentMember.isEmpty())
        if (name == "class") "$name\$" else name // TODO: use a list of Java keywords here
    else
        "${parentMember}_$name"

    /** hidden if false, contributes to layout only */
    var public = true
    /** mutable if true, even if the parent struct is not mutable */
    var mutable = false

    internal var links: String = ""
    internal var linkMode: LinkMode = LinkMode.SINGLE

    fun links(links: String, linkMode: LinkMode = LinkMode.SINGLE) {
        this.links = links
        this.linkMode = linkMode
    }
}

internal class StructMemberBuffer(
    nativeType: PointerType,
    name: String,
    documentation: String
) : StructMember(nativeType, name, documentation)

private open class StructMemberArray(
    nativeType: NativeType,
    name: String,
    documentation: String,
    /** Number of elements in the array. */
    val size: String,
    /** Number of pointer elements that must not be null. */
    val validSize: String
) : StructMember(nativeType, name, documentation) {

    val primitiveMapping get() = this.nativeType.let {
        if (it is PointerType) PrimitiveMapping.POINTER else it.mapping as PrimitiveMapping
    }

}

private class StructMemberCharArray(
    nativeType: CharType,
    name: String,
    documentation: String,
    size: String
) : StructMemberArray(nativeType, name, documentation, size, size)

private class StructMemberPadding(size: String, val condition: String?) : StructMemberArray(char, ANONYMOUS, "", size, size) {
    init {
        public = false
    }
}

private enum class MultiSetterMode {
    NORMAL,
    ALTER
}

private enum class AccessMode(val indent: String) {
    INSTANCE(t),
    FLYWEIGHT("$t$t");
}

class Struct(
    packageName: String,
    className: String,
    nativeSubPath: String = "",
    /** The native struct name. May be different than className. */
    val nativeName: String,
    private val union: Boolean,
    /** when true, a declaration is missing, we need to output one. */
    private val virtual: Boolean,
    /** when false, setters methods will not be generated. */
    private val mutable: Boolean,
    /** if specified, is the super-type of this struct. */
    private val extends: Struct?,
    /** when true, the struct layout will be built using native code. */
    private val nativeLayout: Boolean
) : GeneratorTargetNative(packageName, className, nativeSubPath) {

    companion object {
        private val bufferMethodMap = mapOf(
            "boolean" to "Byte",

            "byte" to "Byte",
            "char" to "Char",
            "short" to "Short",
            "int" to "Int",
            "long" to "Long",

            "float" to "Float",
            "double" to "Double"
        )

        private const val BUFFER_CAPACITY_PARAM = "capacity"
    }

    val nativeType get() = StructType(this)

    /* Output parameter or function result by value */
    private var usageOutput = false

    fun hasUsageOutput() {
        usageOutput = true
    }

    /* Input parameter */
    private var usageInput = false

    fun hasUsageInput() {
        usageInput = true
    }

    /* Function result by reference */
    private var usageResultPointer = false

    fun hasUsageResultPointer() {
        usageResultPointer = true
    }

    private var static: String? = null
    fun static(expression: String) {
        static = expression
    }

    private val members = ArrayList<StructMember>()

    private val visibleMembers
        get() = members.asSequence().filter { it !is StructMemberPadding }

    private val publicMembers
        get() = members.asSequence().filter { it.public }

    private fun mutableMembers(members: Sequence<StructMember> = publicMembers): Sequence<StructMember> = members.let {
        if (mutable) it else it.filter { member -> member.mutable }
    }

    private val settableMembers: Sequence<StructMember> by lazy(LazyThreadSafetyMode.NONE) {
        val mutableMembers = mutableMembers()
        mutableMembers.filter { !it.has<AutoSizeMember>() || it.get<AutoSizeMember>().keepSetter(mutableMembers) }
    }

    private fun hasMutableMembers(members: Sequence<StructMember> = publicMembers) = this.members.isNotEmpty() && (mutable || mutableMembers(members).any())

    operator fun get(name: String): StructMember = members.asSequence().first { it.name == name }

    fun StructMember.replace(old: StructMember) {
        members.remove(this)
        members[members.indexOf(old)] = this
    }

    private fun add(member: StructMember): StructMember {
        members.add(member)
        return member
    }

    // Plain field
    fun NativeType.member(name: String, documentation: String) = add(StructMember(this, name, documentation))

    // Points to an array of structs, not a single struct
    fun PointerType.buffer(name: String, documentation: String): StructMember {
        if (this.elementType !is StructType)
            throw IllegalArgumentException("Only pointer-to-struct types can be used as buffer struct members")

        return add(StructMemberBuffer(this, name, documentation))
    }

    // Array field
    fun NativeType.array(name: String, documentation: String, size: Int, validSize: Int = size)
        = array(name, documentation, size.toString(), validSize.toString())

    fun NativeType.array(name: String, documentation: String, size: String, validSize: String = size)
        = add(StructMemberArray(this, name, documentation, size, validSize))

    // CharSequence special-case
    fun CharType.array(name: String, documentation: String, size: Int) = array(name, documentation, size.toString())

    fun CharType.array(name: String, documentation: String, size: String) = add(StructMemberCharArray(this, name, documentation, size))

    fun padding(size: Int, condition: String? = null) = padding(size.toString(), condition)
    fun padding(size: String, condition: String? = null) = add(StructMemberPadding(size, condition))

    /** Anonymous nested member struct definition. */
    fun struct(init: Struct.() -> Unit): StructMember = struct(ANONYMOUS, ANONYMOUS, init)

    /** Nested member struct definition. */
    fun struct(name: String, documentation: String, init: Struct.() -> Unit): StructMember {
        val struct = Struct(ANONYMOUS, ANONYMOUS, "", ANONYMOUS, false, false, mutable, null, false)
        struct.init()
        return StructType(struct).member(name, documentation)
    }

    /** Anonymous nested member union definition. */
    fun union(init: Struct.() -> Unit): StructMember = union(ANONYMOUS, ANONYMOUS, init)

    /** Nested member union definition. */
    fun union(name: String, documentation: String, init: Struct.() -> Unit): StructMember {
        val struct = Struct(ANONYMOUS, ANONYMOUS, "", ANONYMOUS, true, false, mutable, null, false)
        struct.init()
        return StructType(struct).member(name, documentation)
    }

    /** The nested struct's members are embedded in the parent struct. */
    private val StructMember.isNestedStruct
        get() = nativeType is StructType && this !is StructMemberArray

    /** The nested struct is not defined elsewhere, it's part of the parent struct's definition */
    private val StructMember.isNestedStructDefinition
        get() = isNestedStruct && (nativeType as StructType).name === ANONYMOUS

    private val StructMember.nestedMembers
        get() = (nativeType as StructType).definition.visibleMembers

    private val containsUnion: Boolean get() = union || members.any {
        it.isNestedStruct && (it.nativeType as StructType).let { it.name === ANONYMOUS && it.definition.containsUnion }
    }

    internal val validations: Sequence<String> by lazy(LazyThreadSafetyMode.NONE) {
        if (union)
            return@lazy emptySequence<String>()

        val validations = ArrayList<String>()

        fun MutableList<String>.addPointer(m: StructMember) = this.add("$t${t}long ${m.name} = memGetAddress($STRUCT + $className.${m.offsetField});")
        fun MutableList<String>.addCount(m: StructMember) = this.add("$t$t${m.nativeType.javaMethodType} ${m.name} = n${m.name}($STRUCT);")

        fun validate(m: StructMember, indent: String, hasPointer: Boolean = false): String {
            return if (m.nativeType.hasStructValidation) {
                if (m is StructMemberArray) {
                    """${
                    if (hasPointer) "" else "${indent}long ${m.name} = $STRUCT + $className.${m.offsetField};\n"
                    }${indent}for (int i = 0; i < ${getReferenceMember<AutoSizeMember>(m.name)?.name ?: m.size}; i++) {${
                    if (m is PointerType) {
                        if (m.validSize == m.size)
                            "\n$indent   check(memGetAddress(${m.name}));"
                        else
                            """
$indent   if (i < ${m.validSize}) {
$indent       check(memGetAddress(${m.name}));
$indent   } else if (memGetAddress(${m.name}) == NULL) {
$indent       break;
$indent   }"""
                    } else ""}
$indent   ${m.nativeType.javaMethodType}.validate(${m.name});
$indent   ${m.name} += POINTER_SIZE;
$indent}"""
                } else {
                    "${if (hasPointer) "" else
                        "${indent}long ${m.name} = memGetAddress($STRUCT + $className.${m.offsetField});\n" +
                        "${indent}check(${m.name});\n"
                    }$indent${m.nativeType.javaMethodType}.validate(${m.name}${getReferenceMember<AutoSizeMember>(m.name).let { if (it != null) ", ${it.name}" else "" }});"
                }
            } else
                "${indent}check(memGetAddress($STRUCT + $className.${m.offsetField}));"
        }

        fun validationBlock(condition: String, validations: String): String = "$t${t}if ($condition) {\n$validations\n$t$t}"

        mutableMembers().forEach { m ->
            if (m.has<AutoSizeMember>()) {
                m.get<AutoSizeMember>().let { autoSize ->
                    val refs = autoSize.members(mutableMembers())

                    if (autoSize.atLeastOne) {
                        // TODO: There will be redundancy here when one of the refs is a validateable struct array. But we don't have a case for it yet.
                        validations.addCount(m)

                        // if m != 0, make sure one of the auto-sized members is not null
                        validations.add(
                            "$t${t}if (${if (autoSize.optional) "\n$t$t$t${m.name} != 0 &&" else "\n$t$t$t${m.name} == 0 || ("}${
                            refs.map { "\n$t$t${t}memGetAddress($STRUCT + $className.${it.offsetField}) == NULL" }.joinToString(" &&")
                            }\n$t$t${if (autoSize.optional) "" else ")"}) {\n$t$t${t}throw new NullPointerException(\"At least one of ${refs.map { it.name }.joinToString()} must not be null\");\n$t$t}"
                        )
                    } else if (autoSize.optional) {
                        val refValidations = refs.filter { !it.has(nullable) }.map { ref ->
                            validate(ref, "$t$t$t")
                        }.joinToString("\n")

                        if (refValidations.isEmpty())
                            return@let

                        // if m != 0, make sure auto-sized members are not null
                        validations.add(
                            validationBlock("${if (refValidations.contains(", ${m.name})")) {
                                validations.addCount(m)
                                m.name
                            } else "n${m.name}($STRUCT)"} != 0", refValidations)
                        )
                    } else if (refs.any { it.nativeType.hasStructValidation })
                        validations.addCount(m)
                }
            } else if (m.nativeType is StructType && m.nativeType.definition.validations.any()) {
                validations.add(
                    if (m is StructMemberArray)
                        validate(m, "$t$t")
                    else
                        "$t$t${m.nativeType.javaMethodType}.validate($STRUCT + $className.${m.offsetField});"
                )
            } else if (m.nativeType is PointerType && getReferenceMember<AutoSizeMember>(m.name)?.get<AutoSizeMember>().let { it == null || !it.optional }) {
                if (m.nativeType.hasStructValidation) {
                    validations.add(
                        if (m.has(nullable)) {
                            validations.addPointer(m)
                            validationBlock("${m.name} != NULL", validate(m, "$t$t$t", hasPointer = true))
                        } else
                            validate(m, "$t$t")
                    )
                } else if (!m.has(nullable) && m.nativeType !is StructType) {
                    validations.add(validate(m, "$t$t"))
                }
            }
        }

        validations.asSequence()
    }

    /** Returns a member that has the specified ReferenceModifier with the specified reference. Returns null if no such parameter exists. */
    private inline fun <reified T> getReferenceMember(reference: String) where T : StructMemberModifier, T : ReferenceModifier = members.firstOrNull {
        it.has<T>(reference)
    } // Assumes at most 1 parameter will be found that references the specified parameter

    private fun PrintWriter.printDocumentation() {
        val builder = StringBuilder()

        val members = members.filter { it !is StructMemberPadding }
        if (documentation != null) {
            builder.append(documentation)
            if (members.isNotEmpty())
                builder.append("\n\n")
        }

        if (members.isNotEmpty()) {
            val memberDoc = printMemberDocumentation()
            if (memberDoc.isNotEmpty())
                builder
                    .append("<h3>Member documentation</h3>\n\n")
                    .append(ul(*memberDoc.toTypedArray()))
                    .append("\n\n")

            builder.append("<h3>Layout</h3>\n\n")
            builder.append(codeBlock(printStructLayout()))
        }

        if (builder.isNotEmpty())
            println(processDocumentation(builder.toString()).toJavaDoc(indentation = ""))
    }

    private val nativeNameQualified get() = (if (union) "union " else "struct ").let { type ->
        when {
            nativeName.startsWith(type) -> nativeName
            nativeName === ANONYMOUS    -> type.substring(0, type.length - 1)
            else                        -> "$type$nativeName"
        }
    }

    private fun Struct.printStructLayout(indentation: String = ""): String {
        val memberIndentation = "$indentation    "
        return """$nativeNameQualified {
${members.joinToString("\n$memberIndentation", prefix = memberIndentation) {
            if (it.isNestedStructDefinition)
                "${(it.nativeType as StructType).definition.printStructLayout(memberIndentation)}${if (it.name === ANONYMOUS) "" else " ${it.name}"};"
            else {
                val nativeType = it.nativeType.let {
                    (
                        if (it is PointerType && !it.includesPointer)
                            "${it.name}${if (!it.name.endsWith('*')) " " else ""}*"
                        else
                            it.name
                    ).let { nativeName ->
                        var elementType = it
                        while (true) {
                            if (elementType !is PointerType || elementType.elementType == null)
                                break

                            elementType = elementType.elementType!!
                        }

                        if (elementType is StructType || elementType is CallbackType)
                            nativeName.replace(elementType.name, "{@link ${elementType.javaMethodType} ${elementType.name}}")
                        else
                            nativeName
                    }
                }
                "${if (it has ConstMember) "const " else ""}$nativeType${if (it.name === ANONYMOUS) "" else " ${it.name}"}${if (it is StructMemberArray) "[${it.size}]" else ""};"
            }
        }}
$indentation}"""
    }

    private fun Struct.printMemberDocumentation(prefix: String = "", documentation: MutableList<String> = ArrayList()): List<String> {
        members.forEach {
            if (it.isNestedStructDefinition)
                (it.nativeType as StructType).definition.printMemberDocumentation(if (it.name === ANONYMOUS) prefix else "$prefix${it.name}.", documentation)
            else if (it.documentation.isNotEmpty() || it.links.isNotEmpty()) {
                val doc = if (it.links.isEmpty())
                    it.documentation
                else
                    it.linkMode.appendLinks(
                        it.documentation,
                        if (!it.links.contains('+')) it.links else linksFromRegex(it.links)
                    )
                documentation.add("{@code $prefix${it.name}} &ndash; $doc")
            }
        }
        return documentation
    }

    private fun StructMember.error(msg: String) {
        throw IllegalArgumentException("$msg [${this<EMAIL>}, member: $name]")
    }

    private fun validate(mallocable: Boolean) {
        if (mallocable) {
            members.forEach {
                if (it.nativeType is PointerType && it.nativeType.elementType is StructType)
                    it.nativeType.elementType.definition.hasUsageInput()
            }
        }

        members.filter { it.has<AutoSizeMember>() }.forEach {
            val autoSize = it.get<AutoSizeMember>()

            autoSize.references.forEach { reference ->
                val bufferParam = members.firstOrNull { it.name == reference }
                if (bufferParam == null)
                    it.error("Reference does not exist: AutoSize($reference)")
                else {
                    if (bufferParam !is StructMemberArray) {
                        if (bufferParam.nativeType !is PointerType)
                            it.error("Reference must be a pointer type: AutoSize($reference)")

                        if (!bufferParam.nativeType.isPointerData)
                            it.error("Reference must not be a opaque pointer: AutoSize($reference)")

                        if ((bufferParam.nativeType as PointerType).elementType is StructType && bufferParam !is StructMemberBuffer)
                            it.error("Reference must be a struct buffer: AutoSize($reference)")
                    } else if (autoSize.optional || autoSize.atLeastOne)
                        it.error("Optional cannot be used with array references: AutoSize($reference)")

                    if (autoSize.atLeastOne && !bufferParam.has(nullable))
                        it.error("The \"atLeastOne\" option requires references to be nullable: AutoSize($reference)")
                }
            }
        }
    }

    override fun PrintWriter.generateJava() {
        val mallocable = mutable || usageOutput || (usageInput && !usageResultPointer)
        validate(mallocable)

        val nativeLayout = this@Struct.nativeLayout || members.isEmpty()
        if (!nativeLayout && preamble.hasNativeDirectives)
            kotlin.io.println("${t}Unnecessary native directives in struct: $packageName.$className")

        print(HEADER)
        println("package $packageName;\n")

        println("import java.nio.*;\n")

        if (mallocable || members.any { m -> m.nativeType.let { it is PointerType && it.mapping === PointerMapping.DATA_POINTER && (it.elementType !is StructType || m is StructMemberArray) } })
            println("import org.lwjgl.*;")
        println("import org.lwjgl.system.*;\n")

{'fraction_non_alphanumeric': 0.11869133946111124, 'fraction_numerical': 0.0006305628986491402, 'mean_word_length': 2.1368581209585393, 'pattern_counts': {'":': 0, '<': 66, '<?xml version=': 0, '>': 92, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 3, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1564251', 'n_tokens_mistral': 11051, 'n_tokens_neox': 10639, 'n_words': 2826}