276 lines
5.6 KiB
Go
276 lines
5.6 KiB
Go
// Copyright 2024 The NATS Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Inspired by https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protowire/wire.go
|
|
|
|
package server
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
var errProtoInsufficient = errors.New("insufficient data to read a value")
|
|
var errProtoOverflow = errors.New("too much data for a value")
|
|
var errProtoInvalidFieldNumber = errors.New("invalid field number")
|
|
|
|
func protoScanField(b []byte) (num, typ, size int, err error) {
|
|
num, typ, sizeTag, err := protoScanTag(b)
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
b = b[sizeTag:]
|
|
|
|
sizeValue, err := protoScanFieldValue(typ, b)
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
return num, typ, sizeTag + sizeValue, nil
|
|
}
|
|
|
|
func protoScanTag(b []byte) (num, typ, size int, err error) {
|
|
tagint, size, err := protoScanVarint(b)
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
|
|
// NOTE: MessageSet allows for larger field numbers than normal.
|
|
if (tagint >> 3) > uint64(math.MaxInt32) {
|
|
return 0, 0, 0, errProtoInvalidFieldNumber
|
|
}
|
|
num = int(tagint >> 3)
|
|
if num < 1 {
|
|
return 0, 0, 0, errProtoInvalidFieldNumber
|
|
}
|
|
typ = int(tagint & 7)
|
|
|
|
return num, typ, size, nil
|
|
}
|
|
|
|
func protoScanFieldValue(typ int, b []byte) (size int, err error) {
|
|
switch typ {
|
|
case 0:
|
|
_, size, err = protoScanVarint(b)
|
|
case 5: // fixed32
|
|
if len(b) < 4 {
|
|
return 0, errProtoInsufficient
|
|
}
|
|
size = 4
|
|
case 1: // fixed64
|
|
if len(b) < 8 {
|
|
return 0, errProtoInsufficient
|
|
}
|
|
size = 8
|
|
case 2: // length-delimited
|
|
size, err = protoScanBytes(b)
|
|
default:
|
|
return 0, fmt.Errorf("unsupported type: %d", typ)
|
|
}
|
|
return size, err
|
|
}
|
|
|
|
func protoScanVarint(b []byte) (v uint64, size int, err error) {
|
|
var y uint64
|
|
if len(b) <= 0 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
v = uint64(b[0])
|
|
if v < 0x80 {
|
|
return v, 1, nil
|
|
}
|
|
v -= 0x80
|
|
|
|
if len(b) <= 1 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[1])
|
|
v += y << 7
|
|
if y < 0x80 {
|
|
return v, 2, nil
|
|
}
|
|
v -= 0x80 << 7
|
|
|
|
if len(b) <= 2 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[2])
|
|
v += y << 14
|
|
if y < 0x80 {
|
|
return v, 3, nil
|
|
}
|
|
v -= 0x80 << 14
|
|
|
|
if len(b) <= 3 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[3])
|
|
v += y << 21
|
|
if y < 0x80 {
|
|
return v, 4, nil
|
|
}
|
|
v -= 0x80 << 21
|
|
|
|
if len(b) <= 4 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[4])
|
|
v += y << 28
|
|
if y < 0x80 {
|
|
return v, 5, nil
|
|
}
|
|
v -= 0x80 << 28
|
|
|
|
if len(b) <= 5 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[5])
|
|
v += y << 35
|
|
if y < 0x80 {
|
|
return v, 6, nil
|
|
}
|
|
v -= 0x80 << 35
|
|
|
|
if len(b) <= 6 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[6])
|
|
v += y << 42
|
|
if y < 0x80 {
|
|
return v, 7, nil
|
|
}
|
|
v -= 0x80 << 42
|
|
|
|
if len(b) <= 7 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[7])
|
|
v += y << 49
|
|
if y < 0x80 {
|
|
return v, 8, nil
|
|
}
|
|
v -= 0x80 << 49
|
|
|
|
if len(b) <= 8 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[8])
|
|
v += y << 56
|
|
if y < 0x80 {
|
|
return v, 9, nil
|
|
}
|
|
v -= 0x80 << 56
|
|
|
|
if len(b) <= 9 {
|
|
return 0, 0, errProtoInsufficient
|
|
}
|
|
y = uint64(b[9])
|
|
v += y << 63
|
|
if y < 2 {
|
|
return v, 10, nil
|
|
}
|
|
return 0, 0, errProtoOverflow
|
|
}
|
|
|
|
func protoScanBytes(b []byte) (size int, err error) {
|
|
l, lenSize, err := protoScanVarint(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if l > uint64(len(b[lenSize:])) {
|
|
return 0, errProtoInsufficient
|
|
}
|
|
return lenSize + int(l), nil
|
|
}
|
|
|
|
func protoEncodeVarint(v uint64) []byte {
|
|
b := make([]byte, 0, 10)
|
|
switch {
|
|
case v < 1<<7:
|
|
b = append(b, byte(v))
|
|
case v < 1<<14:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte(v>>7))
|
|
case v < 1<<21:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte(v>>14))
|
|
case v < 1<<28:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte((v>>14)&0x7f|0x80),
|
|
byte(v>>21))
|
|
case v < 1<<35:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte((v>>14)&0x7f|0x80),
|
|
byte((v>>21)&0x7f|0x80),
|
|
byte(v>>28))
|
|
case v < 1<<42:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte((v>>14)&0x7f|0x80),
|
|
byte((v>>21)&0x7f|0x80),
|
|
byte((v>>28)&0x7f|0x80),
|
|
byte(v>>35))
|
|
case v < 1<<49:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte((v>>14)&0x7f|0x80),
|
|
byte((v>>21)&0x7f|0x80),
|
|
byte((v>>28)&0x7f|0x80),
|
|
byte((v>>35)&0x7f|0x80),
|
|
byte(v>>42))
|
|
case v < 1<<56:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte((v>>14)&0x7f|0x80),
|
|
byte((v>>21)&0x7f|0x80),
|
|
byte((v>>28)&0x7f|0x80),
|
|
byte((v>>35)&0x7f|0x80),
|
|
byte((v>>42)&0x7f|0x80),
|
|
byte(v>>49))
|
|
case v < 1<<63:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte((v>>14)&0x7f|0x80),
|
|
byte((v>>21)&0x7f|0x80),
|
|
byte((v>>28)&0x7f|0x80),
|
|
byte((v>>35)&0x7f|0x80),
|
|
byte((v>>42)&0x7f|0x80),
|
|
byte((v>>49)&0x7f|0x80),
|
|
byte(v>>56))
|
|
default:
|
|
b = append(b,
|
|
byte((v>>0)&0x7f|0x80),
|
|
byte((v>>7)&0x7f|0x80),
|
|
byte((v>>14)&0x7f|0x80),
|
|
byte((v>>21)&0x7f|0x80),
|
|
byte((v>>28)&0x7f|0x80),
|
|
byte((v>>35)&0x7f|0x80),
|
|
byte((v>>42)&0x7f|0x80),
|
|
byte((v>>49)&0x7f|0x80),
|
|
byte((v>>56)&0x7f|0x80),
|
|
1)
|
|
}
|
|
return b
|
|
}
|