mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-14 07:59:31 +01:00
113 lines
2.5 KiB
Go
113 lines
2.5 KiB
Go
|
package ber
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
func readIdentifier(reader io.Reader) (Identifier, int, error) {
|
||
|
identifier := Identifier{}
|
||
|
read := 0
|
||
|
|
||
|
// identifier byte
|
||
|
b, err := readByte(reader)
|
||
|
if err != nil {
|
||
|
if Debug {
|
||
|
fmt.Printf("error reading identifier byte: %v\n", err)
|
||
|
}
|
||
|
return Identifier{}, read, err
|
||
|
}
|
||
|
read++
|
||
|
|
||
|
identifier.ClassType = Class(b) & ClassBitmask
|
||
|
identifier.TagType = Type(b) & TypeBitmask
|
||
|
|
||
|
if tag := Tag(b) & TagBitmask; tag != HighTag {
|
||
|
// short-form tag
|
||
|
identifier.Tag = tag
|
||
|
return identifier, read, nil
|
||
|
}
|
||
|
|
||
|
// high-tag-number tag
|
||
|
tagBytes := 0
|
||
|
for {
|
||
|
b, err := readByte(reader)
|
||
|
if err != nil {
|
||
|
if Debug {
|
||
|
fmt.Printf("error reading high-tag-number tag byte %d: %v\n", tagBytes, err)
|
||
|
}
|
||
|
return Identifier{}, read, err
|
||
|
}
|
||
|
tagBytes++
|
||
|
read++
|
||
|
|
||
|
// Lowest 7 bits get appended to the tag value (x.690, 8.1.2.4.2.b)
|
||
|
identifier.Tag <<= 7
|
||
|
identifier.Tag |= Tag(b) & HighTagValueBitmask
|
||
|
|
||
|
// First byte may not be all zeros (x.690, 8.1.2.4.2.c)
|
||
|
if tagBytes == 1 && identifier.Tag == 0 {
|
||
|
return Identifier{}, read, errors.New("invalid first high-tag-number tag byte")
|
||
|
}
|
||
|
// Overflow of int64
|
||
|
// TODO: support big int tags?
|
||
|
if tagBytes > 9 {
|
||
|
return Identifier{}, read, errors.New("high-tag-number tag overflow")
|
||
|
}
|
||
|
|
||
|
// Top bit of 0 means this is the last byte in the high-tag-number tag (x.690, 8.1.2.4.2.a)
|
||
|
if Tag(b)&HighTagContinueBitmask == 0 {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return identifier, read, nil
|
||
|
}
|
||
|
|
||
|
func encodeIdentifier(identifier Identifier) []byte {
|
||
|
b := []byte{0x0}
|
||
|
b[0] |= byte(identifier.ClassType)
|
||
|
b[0] |= byte(identifier.TagType)
|
||
|
|
||
|
if identifier.Tag < HighTag {
|
||
|
// Short-form
|
||
|
b[0] |= byte(identifier.Tag)
|
||
|
} else {
|
||
|
// high-tag-number
|
||
|
b[0] |= byte(HighTag)
|
||
|
|
||
|
tag := identifier.Tag
|
||
|
|
||
|
b = append(b, encodeHighTag(tag)...)
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func encodeHighTag(tag Tag) []byte {
|
||
|
// set cap=4 to hopefully avoid additional allocations
|
||
|
b := make([]byte, 0, 4)
|
||
|
for tag != 0 {
|
||
|
// t := last 7 bits of tag (HighTagValueBitmask = 0x7F)
|
||
|
t := tag & HighTagValueBitmask
|
||
|
|
||
|
// right shift tag 7 to remove what was just pulled off
|
||
|
tag >>= 7
|
||
|
|
||
|
// if b already has entries this entry needs a continuation bit (0x80)
|
||
|
if len(b) != 0 {
|
||
|
t |= HighTagContinueBitmask
|
||
|
}
|
||
|
|
||
|
b = append(b, byte(t))
|
||
|
}
|
||
|
// reverse
|
||
|
// since bits were pulled off 'tag' small to high the byte slice is in reverse order.
|
||
|
// example: tag = 0xFF results in {0x7F, 0x01 + 0x80 (continuation bit)}
|
||
|
// this needs to be reversed into 0x81 0x7F
|
||
|
for i, j := 0, len(b)-1; i < len(b)/2; i++ {
|
||
|
b[i], b[j-i] = b[j-i], b[i]
|
||
|
}
|
||
|
return b
|
||
|
}
|