Skip to content

clipperhouse/displaywidth

Repository files navigation

displaywidth

A high-performance Go package for measuring the monospace display width of strings, UTF-8 bytes, and runes.

Documentation Test Fuzz

Install

go get github.com/clipperhouse/displaywidth

Usage

package main

import (
    "fmt"
    "github.com/clipperhouse/displaywidth"
)

func main() {
    width := displaywidth.String("Hello, 世界!")
    fmt.Println(width)

    width = displaywidth.Bytes([]byte("🌍"))
    fmt.Println(width)

    width = displaywidth.Rune('🌍')
    fmt.Println(width)
}

For most purposes, you should use the String or Bytes methods.

Options

You can specify East Asian Width settings. When false (default), East Asian Ambiguous characters are treated as width 1. When true, East Asian Ambiguous characters are treated as width 2.

myOptions := displaywidth.Options{
    EastAsianWidth: true,
}

width := myOptions.String("Hello, 世界!")
fmt.Println(width)

Technical details

This package implements the Unicode East Asian Width standard (UAX #11), and handles version selectors, and regional indicator pairs (flags). We implement Unicode TR51.

clipperhouse/displaywidth, mattn/go-runewidth, and rivo/uniseg will give the same outputs for most real-world text. See extensive details in the compatibility analysis.

If you wish to investigate the core logic, see the lookupProperties and width functions in width.go. The essential trie generation logic is in buildPropertyBitmap in unicode.go.

I (@clipperhouse) am keeping an eye on emerging standards and test suites.

Prior Art

mattn/go-runewidth

rivo/uniseg

x/text/width

x/text/internal/triegen

Benchmarks

cd comparison
go test -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: github.com/clipperhouse/displaywidth/comparison
cpu: Apple M2

BenchmarkString_Mixed/clipperhouse/displaywidth-8     	     10469 ns/op	   161.15 MB/s      0 B/op      0 allocs/op
BenchmarkString_Mixed/mattn/go-runewidth-8            	     14250 ns/op	   118.39 MB/s      0 B/op      0 allocs/op
BenchmarkString_Mixed/rivo/uniseg-8                   	     19258 ns/op	    87.60 MB/s      0 B/op      0 allocs/op

BenchmarkString_EastAsian/clipperhouse/displaywidth-8 	     10518 ns/op	   160.39 MB/s      0 B/op      0 allocs/op
BenchmarkString_EastAsian/mattn/go-runewidth-8        	     23827 ns/op	    70.80 MB/s      0 B/op      0 allocs/op
BenchmarkString_EastAsian/rivo/uniseg-8               	     19537 ns/op	    86.35 MB/s      0 B/op      0 allocs/op

BenchmarkString_ASCII/clipperhouse/displaywidth-8     	      1027 ns/op	   124.61 MB/s      0 B/op      0 allocs/op
BenchmarkString_ASCII/mattn/go-runewidth-8            	      1166 ns/op	   109.78 MB/s      0 B/op      0 allocs/op
BenchmarkString_ASCII/rivo/uniseg-8                   	      1551 ns/op	    82.52 MB/s      0 B/op      0 allocs/op

BenchmarkString_Emoji/clipperhouse/displaywidth-8     	      3164 ns/op	   228.84 MB/s      0 B/op      0 allocs/op
BenchmarkString_Emoji/mattn/go-runewidth-8            	      4728 ns/op	   153.13 MB/s      0 B/op      0 allocs/op
BenchmarkString_Emoji/rivo/uniseg-8                   	      6489 ns/op	   111.57 MB/s      0 B/op      0 allocs/op

BenchmarkRune_Mixed/clipperhouse/displaywidth-8       	      3429 ns/op	   491.96 MB/s      0 B/op      0 allocs/op
BenchmarkRune_Mixed/mattn/go-runewidth-8              	      5308 ns/op	   317.81 MB/s      0 B/op      0 allocs/op

BenchmarkRune_EastAsian/clipperhouse/displaywidth-8   	      3419 ns/op	   493.49 MB/s      0 B/op      0 allocs/op
BenchmarkRune_EastAsian/mattn/go-runewidth-8          	     15321 ns/op	   110.11 MB/s      0 B/op      0 allocs/op

BenchmarkRune_ASCII/clipperhouse/displaywidth-8       	       254.4 ns/op	   503.19 MB/s      0 B/op      0 allocs/op
BenchmarkRune_ASCII/mattn/go-runewidth-8              	       264.3 ns/op	   484.31 MB/s      0 B/op      0 allocs/op

BenchmarkRune_Emoji/clipperhouse/displaywidth-8       	      1374 ns/op	   527.02 MB/s      0 B/op      0 allocs/op
BenchmarkRune_Emoji/mattn/go-runewidth-8              	      2210 ns/op	   327.66 MB/s      0 B/op      0 allocs/op

About

Measure the display column width of strings in Go

Topics

Resources

License

Stars

Watchers

Forks

Languages