Skip to content

ASCII Table — Go 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 Go implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Package asciitable is the Go twin of CosmoDev's src/lib/ascii-table.ts (dual
// source: the web lib is TypeScript, the CLI lib is Go — kept in lock-step).
// Pure + deterministic, never panics. The table-driven tests in
// ascii-table_test.go share vectors with src/lib/ascii-table.test.ts so the two
// implementations are held to the same contract.
//
// Each of the 128 ASCII code points is described by its decimal / hex / octal /
// binary forms, a display glyph, a human name, printable / control flags, and
// (for control chars that have one) a C-style escape sequence. The table is a
// pure function of the code point (0–127).
package asciitable

import (
	"fmt"
	"strconv"
	"strings"
)

// Entry describes a single ASCII code point (0–127). It mirrors the Entry
// interface in src/lib/ascii-table.ts field-for-field.
type Entry struct {
	// Dec is the decimal code point, 0–127.
	Dec int
	// Hex is the uppercase hex form, e.g. "0x41".
	Hex string
	// Oct is the zero-padded 3-digit octal form, e.g. "101".
	Oct string
	// Binary is the zero-padded 8-bit binary form, e.g. "01000001".
	Binary string
	// Char is the display glyph: the literal char for printables, a
	// control-picture glyph for controls.
	Char string
	// Name is the human-readable name, e.g. "Line Feed (LF)", "Uppercase A".
	Name string
	// Printable is true for 32–126 (visible or space).
	Printable bool
	// Control is true for 0–31 and 127.
	Control bool
	// Escape is the C-style escape for control chars that have one (e.g. "\n"),
	// nil otherwise — mirroring the TS lib's optional escape field.
	Escape *string
}

// FilterOptions configures FilterTable. It mirrors the filterTable opts object
// in src/lib/ascii-table.ts. The zero value (FilterOptions{}) matches the TS
// default (no query, controls included, full 0–127 range).
//
// Min and Max are *int so the zero value means "default" (Min → 0, Max → 127)
// while an explicit 0 is still honored — exactly like the TS lib's `?? default`.
type FilterOptions struct {
	Query         string
	PrintableOnly bool
	Min           *int // nil → 0
	Max           *int // nil → 127
}

// Official ASCII control-character names (0–31 and 127). Mirrors CONTROL_NAMES.
var controlNames = map[int]string{
	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)",
}

// C-style escape sequences for the control chars that have a standard/common
// one. Mirrors ESCAPES.
var escapes = map[int]string{
	0:  "\\0",
	7:  "\\a",
	8:  "\\b",
	9:  "\\t",
	10: "\\n",
	11: "\\v",
	12: "\\f",
	13: "\\r",
	27: "\\e",
}

// Names for printable punctuation & symbols (letters and digits are derived).
// Mirrors SYMBOL_NAMES.
var symbolNames = map[int]string{
	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",
}

// letterName derives a name for a printable letter or digit. Mirrors
// letterName() in the TS lib.
func letterName(code int) string {
	if code >= 48 && code <= 57 {
		return fmt.Sprintf("Digit %d", code-48)
	}
	if code >= 65 && code <= 90 {
		return fmt.Sprintf("Uppercase %c", rune(code))
	}
	if code >= 97 && code <= 122 {
		return fmt.Sprintf("Lowercase %c", rune(code))
	}
	return string(rune(code))
}

// makeEntry builds a single entry from its code point. Mirrors makeEntry().
func makeEntry(code int) Entry {
	control := code <= 31 || code == 127
	printable := !control
	hex := "0x" + fmt.Sprintf("%02X", code)
	oct := fmt.Sprintf("%03o", code)
	binary := fmt.Sprintf("%08b", code)

	var char string
	if control {
		if code == 127 {
			char = "␡" // U+2421, control picture for DEL
		} else {
			char = string(rune(0x2400 + code))
		}
	} else {
		char = string(rune(code))
	}

	var name string
	if control {
		name = controlNames[code]
	} else if n, ok := symbolNames[code]; ok {
		name = n
	} else {
		name = letterName(code)
	}

	var esc *string
	if control {
		if v, ok := escapes[code]; ok {
			s := v
			esc = &s
		}
	}

	return Entry{
		Dec:       code,
		Hex:       hex,
		Oct:       oct,
		Binary:    binary,
		Char:      char,
		Name:      name,
		Printable: printable,
		Control:   control,
		Escape:    esc,
	}
}

// entries is the full 128-entry ASCII table, indexed by code point. Built once
// at package init; mirrors ENTRY in the TS lib.
var entries = func() []Entry {
	t := make([]Entry, 128)
	for i := range 128 {
		t[i] = makeEntry(i)
	}
	return t
}()

// Table returns the full 128-entry ASCII table, indexed by code point. It is
// the Go twin of ENTRY in src/lib/ascii-table.ts.
func Table() []Entry { return entries }

// GetEntry looks up a single entry by code point. It returns (Entry, false) for
// out-of-range input, mirroring getEntry() returning null in the TS lib. (Go's
// int is always integral, so the TS Number.isInteger check is N/A.)
func GetEntry(code int) (Entry, bool) {
	if code < 0 || code > 127 {
		return Entry{}, false
	}
	return entries[code], true
}

// FilterTable filters 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). It mirrors
// filterTable() in the TS lib.
func FilterTable(opts FilterOptions) []Entry {
	q := strings.ToLower(strings.TrimSpace(opts.Query))
	lo := 0
	if opts.Min != nil {
		lo = *opts.Min
	}
	hi := 127
	if opts.Max != nil {
		hi = *opts.Max
	}

	out := make([]Entry, 0)
	for _, e := range entries {
		if opts.PrintableOnly && !e.Printable {
			continue
		}
		if e.Dec < lo || e.Dec > hi {
			continue
		}
		if q == "" {
			out = append(out, e)
			continue
		}
		escapeStr := ""
		if e.Escape != nil {
			escapeStr = *e.Escape
		}
		hay := []string{
			strconv.Itoa(e.Dec),
			strings.ToLower(e.Hex),
			e.Oct,
			e.Binary,
			strings.ToLower(e.Name),
			strings.ToLower(e.Char),
			escapeStr,
		}
		for _, h := range hay {
			if strings.Contains(h, q) {
				out = append(out, e)
				break
			}
		}
	}
	return out
}

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 →