Skip to content

ASCII Table — Kotlin source

A searchable, filterable reference for all 128 ASCII code points. See the decimal, hex, octal, and binary form of every character, narrow to printable characters only, clamp the code-point range, and click any row to copy. Runs 100% client-side.

This is the Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// ASCII Table — pure logic (Kotlin port): the 128-entry ASCII reference table with
// search and range filters.
//
// Language: Kotlin 1.9 — standard library only (kotlin.text / kotlin.collections).
// Source: CosmoDev polyglot showcase port; canonical = src/lib/ascii-table.ts
//         + this tool's python.py / rust.rs.
// License: display source — part of CosmoDev's polyglot tool pages.
//
// Each of the 128 ASCII code points is described by its decimal / hex / octal /
// binary forms, a display glyph, a human name, whether it is printable, whether
// it is a control character, and (for control chars that have one) a C-style
// escape sequence.

/**
 * A single ASCII code point and its derived representations.
 *
 * @property dec Decimal code point, 0–127.
 * @property hex Uppercase hex, e.g. "0x41".
 * @property oct Zero-padded 3-digit octal, e.g. "101".
 * @property binary Zero-padded 8-bit binary, e.g. "01000001".
 * @property char Display glyph: the literal char for printables, a control-picture glyph otherwise.
 * @property name Human-readable name, e.g. "Line Feed (LF)", "Uppercase A", "Space".
 * @property printable True for 32–126 (visible or space).
 * @property control True for 0–31 and 127.
 * @property escape C-style escape for control chars that have one (e.g. "\n"); null otherwise.
 */
data class Entry(
    val dec: Int,
    val hex: String,
    val oct: String,
    val binary: String,
    val char: String,
    val name: String,
    val printable: Boolean,
    val control: Boolean,
    val escape: String? = null,
)

/** The `ascii-table` tool's pure logic. */
object AsciiTable {

    /**
     * Official ASCII control-character names for 0–31 and 127.
     *
     * Expressed as a `when` over every control code point; the else arm is a
     * defensive guard that documents the precondition (the Rust port uses
     * unreachable!() for the same case).
     */
    private fun controlName(code: Int): String = when (code) {
        0 -> "Null (NUL)"
        1 -> "Start of Heading (SOH)"
        2 -> "Start of Text (STX)"
        3 -> "End of Text (ETX)"
        4 -> "End of Transmission (EOT)"
        5 -> "Enquiry (ENQ)"
        6 -> "Acknowledge (ACK)"
        7 -> "Bell (BEL)"
        8 -> "Backspace (BS)"
        9 -> "Horizontal Tab (HT)"
        10 -> "Line Feed (LF)"
        11 -> "Vertical Tab (VT)"
        12 -> "Form Feed (FF)"
        13 -> "Carriage Return (CR)"
        14 -> "Shift Out (SO)"
        15 -> "Shift In (SI)"
        16 -> "Data Link Escape (DLE)"
        17 -> "Device Control 1 (DC1)"
        18 -> "Device Control 2 (DC2)"
        19 -> "Device Control 3 (DC3)"
        20 -> "Device Control 4 (DC4)"
        21 -> "Negative Acknowledge (NAK)"
        22 -> "Synchronous Idle (SYN)"
        23 -> "End of Transmission Block (ETB)"
        24 -> "Cancel (CAN)"
        25 -> "End of Medium (EM)"
        26 -> "Substitute (SUB)"
        27 -> "Escape (ESC)"
        28 -> "File Separator (FS)"
        29 -> "Group Separator (GS)"
        30 -> "Record Separator (RS)"
        31 -> "Unit Separator (US)"
        127 -> "Delete (DEL)"
        // Only called with control codes; defensive guard for misuse.
        else -> ""
    }

    /**
     * C-style escape for the control chars that own a standard/common one.
     * Returns the literal 2-character source token (backslash + letter), or null.
     */
    private fun controlEscape(code: Int): String? = when (code) {
        0 -> "\\0"
        7 -> "\\a"
        8 -> "\\b"
        9 -> "\\t"
        10 -> "\\n"
        11 -> "\\v"
        12 -> "\\f"
        13 -> "\\r"
        27 -> "\\e"
        else -> null
    }

    /**
     * Name for a printable punctuation / symbol glyph, or null.
     * Letters and digits are derived in [letterName].
     */
    private fun symbolName(code: Int): String? = when (code) {
        32 -> "Space"
        33 -> "Exclamation mark"
        34 -> "Quotation mark"
        35 -> "Number sign"
        36 -> "Dollar sign"
        37 -> "Percent sign"
        38 -> "Ampersand"
        39 -> "Apostrophe"
        40 -> "Left parenthesis"
        41 -> "Right parenthesis"
        42 -> "Asterisk"
        43 -> "Plus sign"
        44 -> "Comma"
        45 -> "Hyphen / Minus"
        46 -> "Full stop"
        47 -> "Slash"
        58 -> "Colon"
        59 -> "Semicolon"
        60 -> "Less-than sign"
        61 -> "Equals sign"
        62 -> "Greater-than sign"
        63 -> "Question mark"
        64 -> "At sign"
        91 -> "Left bracket"
        92 -> "Backslash"
        93 -> "Right bracket"
        94 -> "Circumflex / Caret"
        95 -> "Underscore"
        96 -> "Grave accent"
        123 -> "Left brace"
        124 -> "Vertical bar"
        125 -> "Right brace"
        126 -> "Tilde"
        else -> null
    }

    /** Derive a name for a printable letter or digit. */
    private fun letterName(code: Int): String = when {
        code in 48..57 -> "Digit ${code - 48}"           // '0'..'9'
        code in 65..90 -> "Uppercase ${code.toChar()}"   // 'A'..'Z'
        code in 97..122 -> "Lowercase ${code.toChar()}"  // 'a'..'z'
        else -> code.toChar().toString()
    }

    /** Build a single entry from its code point (0–127). */
    private fun makeEntry(code: Int): Entry {
        val control = code <= 31 || code == 127

        // Control-picture glyph: U+2400 for 0–31, U+2421 ("symbol for delete") for DEL.
        val char = if (control) {
            String(Character.toChars(if (code == 127) 0x2421 else 0x2400 + code))
        } else {
            code.toChar().toString()
        }

        val name = when {
            control -> controlName(code)
            else -> symbolName(code) ?: letterName(code)
        }

        return Entry(
            dec = code,
            hex = "0x%02X".format(code),
            oct = "%03o".format(code),
            binary = code.toString(2).padStart(8, '0'),
            char = char,
            name = name,
            printable = !control,
            control = control,
            escape = if (control) controlEscape(code) else null,
        )
    }

    /**
     * The full 128-entry ASCII table, indexed by code point.
     *
     * Built once: `by lazy` initializes thread-safely on first access and
     * caches for the process lifetime (the Kotlin equivalent of Rust's
     * OnceLock). The list is read-only, so sharing it is safe.
     */
    val entries: List<Entry> by lazy { (0..127).map { makeEntry(it) } }

    /**
     * Look up a single entry by code point.
     *
     * Returns null for out-of-range input. The TypeScript variant returns
     * null; Kotlin's nullable type is the idiomatic equivalent, and the type
     * system forces callers to handle it.
     */
    fun getEntry(code: Int): Entry? = entries.getOrNull(code)

    /**
     * Filter the table.
     *
     * `query` is a case-insensitive substring matched against dec / hex / oct /
     * binary / name / char / escape. `printableOnly` hides controls. `min` /
     * `max` clamp the decimal range (defaults 0–127).
     */
    fun filterTable(
        query: String = "",
        printableOnly: Boolean = false,
        min: Int = 0,
        max: Int = 127,
    ): List<Entry> {
        val q = query.trim().lowercase()
        return entries.filter { e ->
            if (printableOnly && !e.printable) return@filter false
            if (e.dec < min || e.dec > max) return@filter false
            q.isEmpty() || listOf(
                e.dec.toString(),
                e.hex.lowercase(),
                e.oct,
                e.binary,
                e.name.lowercase(),
                e.char.lowercase(),
                e.escape.orEmpty(),
            ).any { it.contains(q) }
        }
    }
}

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →