You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
109 lines
2.3 KiB
109 lines
2.3 KiB
3 years ago
|
package entity
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
"unicode/utf8"
|
||
|
)
|
||
|
|
||
|
// ComputeLength returns length of s encoded as UTF-16 string.
|
||
|
//
|
||
|
// While Telegram API docs state that they expect the number of UTF-8
|
||
|
// code points, in fact they are talking about UTF-16 code units.
|
||
|
func ComputeLength(s string) int {
|
||
|
// From utf16 package.
|
||
|
n := 0
|
||
|
for _, v := range s {
|
||
|
n += utf16RuneLen(v)
|
||
|
}
|
||
|
return n
|
||
|
}
|
||
|
|
||
|
// ComputeLengthBytes returns length of s encoded as UTF-16 string.
|
||
|
//
|
||
|
// While Telegram API docs state that they expect the number of UTF-8
|
||
|
// code points, in fact they are talking about UTF-16 code units.
|
||
|
func ComputeLengthBytes(s []byte) (n int) {
|
||
|
// From utf16 package.
|
||
|
var i int
|
||
|
for i < len(s) {
|
||
|
v, size := utf8.DecodeRune(s[i:])
|
||
|
i += size
|
||
|
n += utf16RuneLen(v)
|
||
|
}
|
||
|
return n
|
||
|
}
|
||
|
|
||
|
func utf16RuneLen(v rune) int {
|
||
|
const (
|
||
|
surrSelf = 0x10000
|
||
|
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
|
||
|
)
|
||
|
|
||
|
if surrSelf <= v && v <= maxRune {
|
||
|
return 2
|
||
|
}
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
func (b *Builder) appendMessage(s string, formats ...Formatter) *Builder {
|
||
|
if s == "" {
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
offset := b.utf16length
|
||
|
length := ComputeLength(s)
|
||
|
|
||
|
b.appendEntities(offset, length, utf8entity{
|
||
|
offset: b.message.Len(),
|
||
|
length: len(s),
|
||
|
}, formats...)
|
||
|
_, _ = b.WriteString(s)
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func (b *Builder) appendEntities(offset, length int, u utf8entity, formats ...Formatter) *Builder {
|
||
|
b.lastFormatIndex = len(b.entities)
|
||
|
for i := range formats {
|
||
|
b.entities = append(b.entities, formats[i](offset, length))
|
||
|
b.lengths = append(b.lengths, u)
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
var _ = []interface {
|
||
|
io.Writer
|
||
|
io.StringWriter
|
||
|
io.ByteWriter
|
||
|
WriteRune(rune) (int, error)
|
||
|
}{
|
||
|
(*Builder)(nil),
|
||
|
}
|
||
|
|
||
|
// Write implements io.Writer.
|
||
|
func (b *Builder) Write(s []byte) (int, error) {
|
||
|
n, err := b.message.Write(s)
|
||
|
b.utf16length += ComputeLengthBytes(s)
|
||
|
return n, err
|
||
|
}
|
||
|
|
||
|
// WriteString implements io.StringWriter.
|
||
|
func (b *Builder) WriteString(s string) (int, error) {
|
||
|
n, err := b.message.WriteString(s)
|
||
|
b.utf16length += ComputeLength(s)
|
||
|
return n, err
|
||
|
}
|
||
|
|
||
|
// WriteByte implements io.ByteWriter.
|
||
|
func (b *Builder) WriteByte(s byte) error {
|
||
|
err := b.message.WriteByte(s)
|
||
|
b.utf16length++
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// WriteRune implements rune writer.
|
||
|
func (b *Builder) WriteRune(s rune) (int, error) {
|
||
|
n, err := b.message.WriteRune(s)
|
||
|
b.utf16length += utf16RuneLen(s)
|
||
|
return n, err
|
||
|
}
|