// Package unistring contains an implementation of a hybrid ASCII/UTF-16 string. // For ASCII strings the underlying representation is equivalent to a normal Go string. // For unicode strings the underlying representation is UTF-16 as []uint16 with 0th element set to 0xFEFF. // unicode.String allows representing malformed UTF-16 values (e.g. stand-alone parts of surrogate pairs) // which cannot be represented in UTF-8. // At the same time it is possible to use unicode.String as property keys just as efficiently as simple strings, // (the leading 0xFEFF ensures there is no clash with ASCII string), and it is possible to convert it // to valueString without extra allocations.
package unistring import ( ) const ( BOM = 0xFEFF ) type String string // Scan checks if the string contains any unicode characters. If it does, converts to an array suitable for creating // a String using FromUtf16, otherwise returns nil. func ( string) []uint16 { := 0 for ; < len(); ++ { if [] >= utf8.RuneSelf { goto } } return nil : for , := range [:] { ++ if > 0xFFFF { ++ } } := make([]uint16, +1) [0] = BOM := 1 for , := range { if <= 0xFFFF { [] = uint16() } else { , := utf16.EncodeRune() [] = uint16() ++ [] = uint16() } ++ } return } func ( string) String { if := Scan(); != nil { return FromUtf16() } return String() } func ( []rune) String { := true := 0 for , := range { if >= utf8.RuneSelf { = false if > 0xFFFF { ++ } } ++ } if { return String() } := make([]uint16, +1) [0] = BOM := 1 for , := range { if <= 0xFFFF { [] = uint16() } else { , := utf16.EncodeRune() [] = uint16() ++ [] = uint16() } ++ } return FromUtf16() } func ( []uint16) String { var string := (*reflect.StringHeader)(unsafe.Pointer(&)) .Data = uintptr(unsafe.Pointer(&[0])) .Len = len() * 2 return String() } func ( String) () string { if := .AsUtf16(); != nil { return string(utf16.Decode([1:])) } return string() } func ( String) () []uint16 { if len() < 4 || len()&1 != 0 { return nil } var []uint16 := string() := (*reflect.SliceHeader)(unsafe.Pointer(&)) .Data = (*reflect.StringHeader)(unsafe.Pointer(&)).Data := len() / 2 .Len = .Cap = if [0] == BOM { return } return nil }