Files
t5de-proxy/internal/imq/messages.go
T

121 lines
2.8 KiB
Go

package imq
import "errors"
// Message types the proxy reads or writes.
const (
C2gSendMessage = 1304
G2cResult = 3102
)
// SendMessage is a C2gSendMessage: a message the client sends to a queue's mount.
type SendMessage struct {
OpID uint64
Queue string
Mount string
Message []byte
}
// field is one protobuf field: its number and either a varint or bytes.
type field struct {
num uint64
varint uint64
bytes []byte
isLen bool
}
func fields(b []byte) ([]field, error) {
var out []field
for i := 0; i < len(b); {
key, n := varint(b[i:])
if n <= 0 {
return nil, ErrMalformed
}
i += n
f := field{num: key >> 3}
switch key & 7 {
case 0:
v, n := varint(b[i:])
if n <= 0 {
return nil, ErrMalformed
}
f.varint, i = v, i+n
case 2:
l, n := varint(b[i:])
if n <= 0 || l > uint64(len(b)-i-n) {
return nil, ErrMalformed
}
i += n
f.bytes, f.isLen, i = b[i:i+int(l)], true, i+int(l)
default:
return nil, ErrMalformed
}
out = append(out, f)
}
return out, nil
}
// ParseSendMessage reads a C2gSendMessage's data.
func ParseSendMessage(data []byte) (SendMessage, error) {
fs, err := fields(data)
if err != nil {
return SendMessage{}, err
}
var m SendMessage
seen := 0
for _, f := range fs {
switch {
case f.num == 1 && !f.isLen:
m.OpID, seen = f.varint, seen|1
case f.num == 2 && f.isLen:
m.Queue, seen = string(f.bytes), seen|2
case f.num == 3 && f.isLen:
m.Mount, seen = string(f.bytes), seen|4
case f.num == 4 && f.isLen:
m.Message, seen = f.bytes, seen|8
default:
return SendMessage{}, errors.New("imq: unexpected field in C2gSendMessage")
}
}
if seen != 15 {
return SendMessage{}, errors.New("imq: C2gSendMessage is missing fields")
}
return m, nil
}
// Marshal encodes the message's data, fields in the client's order.
func (m SendMessage) Marshal() []byte {
var b []byte
b = appendVarintField(b, 1, m.OpID)
b = appendBytesField(b, 2, []byte(m.Queue))
b = appendBytesField(b, 3, []byte(m.Mount))
return appendBytesField(b, 4, m.Message)
}
// Result encodes a G2cResult's data: the answer to the operation opID (status 0 is success).
func Result(opID, status uint64) []byte {
return appendVarintField(appendVarintField(nil, 1, opID), 2, status)
}
// EncodeFrame wraps a message's data in a frame of the given type.
func EncodeFrame(t uint64, data []byte) []byte {
return appendBytesField(appendVarintField(nil, 1, t), 2, data)
}
func appendVarint(b []byte, v uint64) []byte {
for v >= 0x80 {
b = append(b, byte(v)|0x80)
v >>= 7
}
return append(b, byte(v))
}
func appendVarintField(b []byte, num, v uint64) []byte {
return appendVarint(appendVarint(b, num<<3), v)
}
func appendBytesField(b []byte, num uint64, v []byte) []byte {
b = appendVarint(appendVarint(b, num<<3|2), uint64(len(v)))
return append(b, v...)
}