mirror of https://github.com/ftl/tetra-pei
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.4 KiB
Go
134 lines
3.4 KiB
Go
3 years ago
|
package sds
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
/* Text related types and functions */
|
||
|
|
||
|
// TextEncoding enum according to [AI] 29.5.4.1
|
||
|
type TextEncoding byte
|
||
|
|
||
|
// All supported text encoding schemes, according to [AI] table 29.29
|
||
|
const (
|
||
|
Packed7Bit TextEncoding = 0
|
||
|
ISO8859_1 TextEncoding = 1
|
||
|
)
|
||
|
|
||
|
// TextBytes returns the length in bytes of an encoded text with
|
||
|
// the given number of characters and the given encoding
|
||
|
func TextBytes(encoding TextEncoding, length int) int {
|
||
|
bits := TextBytesToBits(encoding, length)
|
||
|
bytes := bits / 8
|
||
|
if bits%8 > 0 {
|
||
|
bytes++
|
||
|
}
|
||
|
return bytes
|
||
|
}
|
||
|
|
||
|
// TextBytesToBits returns the length in bits of an encoded text with
|
||
|
// the given number of characters and the given encoding
|
||
|
func TextBytesToBits(encoding TextEncoding, length int) int {
|
||
|
switch encoding {
|
||
|
case Packed7Bit:
|
||
|
return length*8 - length
|
||
|
default:
|
||
|
return length * 8
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ParseTextHeader in text messages and concatenated text messages.
|
||
|
func ParseTextHeader(bytes []byte) (TextHeader, error) {
|
||
|
if len(bytes) < 1 {
|
||
|
return TextHeader{}, fmt.Errorf("text header too short: %d", len(bytes))
|
||
|
}
|
||
|
|
||
|
var result TextHeader
|
||
|
|
||
|
timestampUsed := (bytes[0] & 0x80) == 0x80
|
||
|
if timestampUsed && len(bytes) < 7 {
|
||
|
return TextHeader{}, fmt.Errorf("text header with timestamp too short: %d", len(bytes))
|
||
|
}
|
||
|
result.Encoding = TextEncoding(bytes[0] & 0x7F)
|
||
|
|
||
|
var timestamp time.Time
|
||
|
var err error
|
||
|
if timestampUsed {
|
||
|
timestamp, err = DecodeTimestamp(bytes[1:4])
|
||
|
if err != nil {
|
||
|
return TextHeader{}, err
|
||
|
}
|
||
|
}
|
||
|
result.Timestamp = timestamp
|
||
|
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
// TextHeader represents the meta information for text used in text messages according to [AI] 29.5.3.3
|
||
|
// and concatenated text messages according to [AI] 29.5.10.3
|
||
|
type TextHeader struct {
|
||
|
Encoding TextEncoding
|
||
|
Timestamp time.Time
|
||
|
}
|
||
|
|
||
|
// Encode this text header
|
||
|
func (h TextHeader) Encode(bytes []byte, bits int) ([]byte, int) {
|
||
|
bytes = append(bytes, byte(h.Encoding))
|
||
|
bits += 8
|
||
|
if !h.Timestamp.IsZero() {
|
||
|
bytes[len(bytes)-1] |= 0x80
|
||
|
bytes = append(bytes, EncodeTimestampUTC(h.Timestamp)...)
|
||
|
bits += 24
|
||
|
}
|
||
|
|
||
|
return bytes, bits
|
||
|
}
|
||
|
|
||
|
// Length returns the length of this text header in bytes.
|
||
|
func (h TextHeader) Length() int {
|
||
|
if h.Timestamp.IsZero() {
|
||
|
return 1
|
||
|
}
|
||
|
return 4
|
||
|
}
|
||
|
|
||
|
// DecodePayloadText decodes the actual text content using the given encoding scheme according to [AI] 29.5.4
|
||
|
func DecodePayloadText(encoding TextEncoding, bytes []byte) (string, error) {
|
||
|
switch encoding {
|
||
|
case ISO8859_1: // only ISO8859-1 at the moment
|
||
|
return decodeISO8859_1(bytes)
|
||
|
default: // be lenient and use ISO8859-1 as fallback
|
||
|
log.Printf("encoding 0x%x is currently not supported, using ISO8859-1 as fallback", encoding)
|
||
|
return decodeISO8859_1(bytes)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func decodeISO8859_1(bytes []byte) (string, error) {
|
||
|
utf8Buf := make([]rune, len(bytes))
|
||
|
for i, b := range bytes {
|
||
|
utf8Buf[i] = rune(b)
|
||
|
}
|
||
|
return string(utf8Buf), nil
|
||
|
}
|
||
|
|
||
|
func AppendEncodedPayloadText(bytes []byte, bits int, text string, encoding TextEncoding) ([]byte, int) {
|
||
|
var encodedBytes []byte
|
||
|
var encodedBits int
|
||
|
switch encoding {
|
||
|
case ISO8859_1: // only ISO8859-1 at the moment
|
||
|
encodedBytes, encodedBits = encodeISO8859_1(text)
|
||
|
default: // be lenient and use ISO8859-1 as fallback
|
||
|
encodedBytes, encodedBits = encodeISO8859_1(text)
|
||
|
}
|
||
|
|
||
|
bytes = append(bytes, encodedBytes...)
|
||
|
bits += encodedBits
|
||
|
return bytes, bits
|
||
|
}
|
||
|
|
||
|
func encodeISO8859_1(text string) ([]byte, int) {
|
||
|
return []byte(text), len(text) * 8
|
||
|
}
|