72 lines
1.8 KiB
Go
72 lines
1.8 KiB
Go
// Copyright (c) 2024 Tulir Asokan
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
// Package confusable implements confusable detection according to UTS #39.
|
|
package confusable
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"golang.org/x/text/unicode/norm"
|
|
)
|
|
|
|
//go:generate go run ./generate.go
|
|
|
|
// Skeleton is the skeleton function defined in UTS #39.
|
|
//
|
|
// If two strings have the same skeleton, they're considered confusable.
|
|
// The skeleton strings are not intended for displaying to users.
|
|
//
|
|
// See https://www.unicode.org/reports/tr39/#Confusable_Detection for more info.
|
|
func Skeleton(input string) string {
|
|
input = norm.NFD.String(input)
|
|
var builder strings.Builder
|
|
builder.Grow(len(input))
|
|
for _, r := range input {
|
|
if !unicode.IsGraphic(r) && r != 0xfffd {
|
|
continue
|
|
}
|
|
repl := GetReplacement(r)
|
|
if repl != "" {
|
|
builder.WriteString(repl)
|
|
} else {
|
|
builder.WriteRune(r)
|
|
}
|
|
}
|
|
return norm.NFD.String(builder.String())
|
|
}
|
|
|
|
// SkeletonBytes is the same as Skeleton, but it returns the skeleton as a byte slice.
|
|
func SkeletonBytes(input string) []byte {
|
|
input = norm.NFD.String(input)
|
|
var builder bytes.Buffer
|
|
builder.Grow(len(input))
|
|
for _, r := range input {
|
|
if !unicode.IsGraphic(r) && r != 0xfffd {
|
|
continue
|
|
}
|
|
repl := GetReplacement(r)
|
|
if repl != "" {
|
|
builder.WriteString(repl)
|
|
} else {
|
|
builder.WriteRune(r)
|
|
}
|
|
}
|
|
return norm.NFD.Bytes(builder.Bytes())
|
|
}
|
|
|
|
// SkeletonHash returns the sha256 hash of the skeleton of the string.
|
|
func SkeletonHash(input string) [32]byte {
|
|
return sha256.Sum256(SkeletonBytes(input))
|
|
}
|
|
|
|
// Confusable checks if two strings are confusable.
|
|
func Confusable(x, y string) bool {
|
|
return Skeleton(x) == Skeleton(y)
|
|
}
|