Initial commit: RegionIO Minecraft server core (26.1.2/protocol 775)
Vanilla-faithful overworld generator (final_density + multi-noise biomes), full connection lifecycle (status/login/configuration/play), chunk streaming, creative block editing, and the protocol/nbt/registry infrastructure.
This commit is contained in:
commit
a7bb9496ae
146 changed files with 217621 additions and 0 deletions
228
internal/protocol/buffer.go
Normal file
228
internal/protocol/buffer.go
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
)
|
||||
|
||||
// ErrShortBuffer is returned when a read would exceed the buffer's contents.
|
||||
var ErrShortBuffer = errors.New("protocol: unexpected end of buffer")
|
||||
|
||||
// Reader decodes typed protocol values from an in-memory packet body.
|
||||
// It tracks a cursor and never reads past the underlying slice.
|
||||
type Reader struct {
|
||||
buf []byte
|
||||
pos int
|
||||
}
|
||||
|
||||
// NewReader returns a Reader over buf. The slice is not copied.
|
||||
func NewReader(buf []byte) *Reader { return &Reader{buf: buf} }
|
||||
|
||||
// Remaining returns the number of unread bytes.
|
||||
func (r *Reader) Remaining() int { return len(r.buf) - r.pos }
|
||||
|
||||
// ReadByte implements io.ByteReader so VarInt helpers can consume the Reader.
|
||||
func (r *Reader) ReadByte() (byte, error) {
|
||||
if r.pos >= len(r.buf) {
|
||||
return 0, ErrShortBuffer
|
||||
}
|
||||
b := r.buf[r.pos]
|
||||
r.pos++
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// readN returns the next n bytes as a sub-slice of the underlying buffer.
|
||||
func (r *Reader) readN(n int) ([]byte, error) {
|
||||
if n < 0 || r.Remaining() < n {
|
||||
return nil, ErrShortBuffer
|
||||
}
|
||||
b := r.buf[r.pos : r.pos+n]
|
||||
r.pos += n
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// VarInt reads a 32-bit VarInt.
|
||||
func (r *Reader) VarInt() (int32, error) {
|
||||
v, _, err := ReadVarInt(r)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// VarLong reads a 64-bit VarLong.
|
||||
func (r *Reader) VarLong() (int64, error) {
|
||||
v, _, err := ReadVarLong(r)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// Bool reads a single-byte boolean.
|
||||
func (r *Reader) Bool() (bool, error) {
|
||||
b, err := r.ReadByte()
|
||||
return b != 0, err
|
||||
}
|
||||
|
||||
// Uint16 reads a big-endian unsigned short.
|
||||
func (r *Reader) Uint16() (uint16, error) {
|
||||
b, err := r.readN(2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint16(b), nil
|
||||
}
|
||||
|
||||
// Int64 reads a big-endian signed long.
|
||||
func (r *Reader) Int64() (int64, error) {
|
||||
b, err := r.readN(8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int64(binary.BigEndian.Uint64(b)), nil
|
||||
}
|
||||
|
||||
// Float64 reads a big-endian IEEE-754 double.
|
||||
func (r *Reader) Float64() (float64, error) {
|
||||
b, err := r.readN(8)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return math.Float64frombits(binary.BigEndian.Uint64(b)), nil
|
||||
}
|
||||
|
||||
// Float32 reads a big-endian IEEE-754 float.
|
||||
func (r *Reader) Float32() (float32, error) {
|
||||
b, err := r.readN(4)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return math.Float32frombits(binary.BigEndian.Uint32(b)), nil
|
||||
}
|
||||
|
||||
// String reads a VarInt-length-prefixed UTF-8 string.
|
||||
func (r *Reader) String() (string, error) {
|
||||
n, err := r.VarInt()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n < 0 || int(n) > MaxStringLen*3 {
|
||||
return "", ErrStringTooLong
|
||||
}
|
||||
b, err := r.readN(int(n))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// Position reads a block position packed into a single long (x:26, z:26, y:12).
|
||||
func (r *Reader) Position() (x, y, z int, err error) {
|
||||
v, err := r.Int64()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
x = int(v >> 38) // top 26 bits, sign-extended
|
||||
y = int(v << 52 >> 52) // low 12 bits, sign-extended
|
||||
z = int(v << 26 >> 38) // middle 26 bits, sign-extended
|
||||
return x, y, z, nil
|
||||
}
|
||||
|
||||
// UUID reads a 128-bit UUID as two big-endian longs (16 bytes).
|
||||
func (r *Reader) UUID() ([16]byte, error) {
|
||||
var u [16]byte
|
||||
b, err := r.readN(16)
|
||||
if err != nil {
|
||||
return u, err
|
||||
}
|
||||
copy(u[:], b)
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Writer accumulates typed protocol values into a byte buffer that becomes a
|
||||
// packet body. The zero value is ready to use.
|
||||
type Writer struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// NewWriter returns a Writer with an optional initial capacity hint.
|
||||
func NewWriter(capacity int) *Writer {
|
||||
return &Writer{buf: make([]byte, 0, capacity)}
|
||||
}
|
||||
|
||||
// Bytes returns the accumulated body. The slice aliases internal storage.
|
||||
func (w *Writer) Bytes() []byte { return w.buf }
|
||||
|
||||
// Len returns the current body length.
|
||||
func (w *Writer) Len() int { return len(w.buf) }
|
||||
|
||||
// VarInt appends a 32-bit VarInt.
|
||||
func (w *Writer) VarInt(v int32) *Writer { w.buf = AppendVarInt(w.buf, v); return w }
|
||||
|
||||
// VarLong appends a 64-bit VarLong.
|
||||
func (w *Writer) VarLong(v int64) *Writer { w.buf = AppendVarLong(w.buf, v); return w }
|
||||
|
||||
// Bool appends a single-byte boolean.
|
||||
func (w *Writer) Bool(v bool) *Writer {
|
||||
if v {
|
||||
w.buf = append(w.buf, 1)
|
||||
} else {
|
||||
w.buf = append(w.buf, 0)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Byte appends a raw byte.
|
||||
func (w *Writer) Byte(v byte) *Writer { w.buf = append(w.buf, v); return w }
|
||||
|
||||
// Uint16 appends a big-endian unsigned short.
|
||||
func (w *Writer) Uint16(v uint16) *Writer {
|
||||
w.buf = binary.BigEndian.AppendUint16(w.buf, v)
|
||||
return w
|
||||
}
|
||||
|
||||
// Int32 appends a big-endian signed int.
|
||||
func (w *Writer) Int32(v int32) *Writer {
|
||||
w.buf = binary.BigEndian.AppendUint32(w.buf, uint32(v))
|
||||
return w
|
||||
}
|
||||
|
||||
// Int64 appends a big-endian signed long.
|
||||
func (w *Writer) Int64(v int64) *Writer {
|
||||
w.buf = binary.BigEndian.AppendUint64(w.buf, uint64(v))
|
||||
return w
|
||||
}
|
||||
|
||||
// Float32 appends a big-endian IEEE-754 float.
|
||||
func (w *Writer) Float32(v float32) *Writer {
|
||||
w.buf = binary.BigEndian.AppendUint32(w.buf, math.Float32bits(v))
|
||||
return w
|
||||
}
|
||||
|
||||
// Float64 appends a big-endian IEEE-754 double.
|
||||
func (w *Writer) Float64(v float64) *Writer {
|
||||
w.buf = binary.BigEndian.AppendUint64(w.buf, math.Float64bits(v))
|
||||
return w
|
||||
}
|
||||
|
||||
// String appends a VarInt-length-prefixed UTF-8 string.
|
||||
func (w *Writer) String(s string) *Writer {
|
||||
w.buf = AppendVarInt(w.buf, int32(len(s)))
|
||||
w.buf = append(w.buf, s...)
|
||||
return w
|
||||
}
|
||||
|
||||
// Position appends a block position packed into a single long (x:26, z:26, y:12).
|
||||
func (w *Writer) Position(x, y, z int) *Writer {
|
||||
v := (int64(x)&0x3FFFFFF)<<38 | (int64(z)&0x3FFFFFF)<<12 | (int64(y) & 0xFFF)
|
||||
return w.Int64(v)
|
||||
}
|
||||
|
||||
// UUID appends a 128-bit UUID verbatim (16 bytes).
|
||||
func (w *Writer) UUID(u [16]byte) *Writer { w.buf = append(w.buf, u[:]...); return w }
|
||||
|
||||
// Raw appends bytes verbatim.
|
||||
func (w *Writer) Raw(b []byte) *Writer { w.buf = append(w.buf, b...); return w }
|
||||
|
||||
// WriteTo writes the accumulated body to dst.
|
||||
func (w *Writer) WriteTo(dst io.Writer) (int64, error) {
|
||||
n, err := dst.Write(w.buf)
|
||||
return int64(n), err
|
||||
}
|
||||
139
internal/protocol/frame.go
Normal file
139
internal/protocol/frame.go
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Packet is a decoded frame: a packet ID plus its raw body bytes.
|
||||
// The body excludes the ID and any length/compression prefixes.
|
||||
type Packet struct {
|
||||
ID int32
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// Body returns a Reader positioned at the start of the packet body.
|
||||
func (p Packet) Body() *Reader { return NewReader(p.Data) }
|
||||
|
||||
// ReadPacket reads one frame from br.
|
||||
//
|
||||
// When threshold < 0 the uncompressed format is used:
|
||||
//
|
||||
// VarInt length | VarInt packet ID | body
|
||||
//
|
||||
// When threshold >= 0 the compressed format is used:
|
||||
//
|
||||
// VarInt packet length | VarInt data length | (zlib or raw) packet ID + body
|
||||
//
|
||||
// A data length of 0 means the payload is stored uncompressed (its
|
||||
// uncompressed size was below the threshold).
|
||||
func ReadPacket(br *bufio.Reader, threshold int32) (Packet, error) {
|
||||
length, _, err := ReadVarInt(br)
|
||||
if err != nil {
|
||||
return Packet{}, err
|
||||
}
|
||||
if length < 0 || int(length) > MaxPacketSize {
|
||||
return Packet{}, ErrPacketTooLarge
|
||||
}
|
||||
|
||||
frame := make([]byte, length)
|
||||
if _, err := io.ReadFull(br, frame); err != nil {
|
||||
return Packet{}, err
|
||||
}
|
||||
|
||||
if threshold < 0 {
|
||||
return parseIDBody(frame)
|
||||
}
|
||||
return parseCompressed(frame)
|
||||
}
|
||||
|
||||
// parseCompressed handles a frame that begins with a Data Length VarInt.
|
||||
func parseCompressed(frame []byte) (Packet, error) {
|
||||
r := NewReader(frame)
|
||||
dataLen, err := r.VarInt()
|
||||
if err != nil {
|
||||
return Packet{}, err
|
||||
}
|
||||
payload := frame[r.pos:]
|
||||
|
||||
if dataLen == 0 {
|
||||
// Stored uncompressed.
|
||||
return parseIDBody(payload)
|
||||
}
|
||||
if dataLen < 0 || int(dataLen) > MaxPacketSize {
|
||||
return Packet{}, ErrPacketTooLarge
|
||||
}
|
||||
|
||||
zr, err := zlib.NewReader(bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return Packet{}, err
|
||||
}
|
||||
defer zr.Close()
|
||||
|
||||
out := make([]byte, dataLen)
|
||||
if _, err := io.ReadFull(zr, out); err != nil {
|
||||
return Packet{}, err
|
||||
}
|
||||
return parseIDBody(out)
|
||||
}
|
||||
|
||||
// parseIDBody splits a VarInt packet ID off the front of buf.
|
||||
func parseIDBody(buf []byte) (Packet, error) {
|
||||
r := NewReader(buf)
|
||||
id, err := r.VarInt()
|
||||
if err != nil {
|
||||
return Packet{}, err
|
||||
}
|
||||
return Packet{ID: id, Data: buf[r.pos:]}, nil
|
||||
}
|
||||
|
||||
// WritePacket writes one frame to w with the given ID and body, using the
|
||||
// uncompressed format when threshold < 0 and the compressed format otherwise.
|
||||
func WritePacket(w io.Writer, threshold int32, id int32, body []byte) error {
|
||||
_, err := w.Write(AppendPacket(nil, threshold, id, body))
|
||||
return err
|
||||
}
|
||||
|
||||
// AppendPacket appends one fully-framed packet to dst and returns the result.
|
||||
// The produced bytes are identical to what WritePacket would write, so callers
|
||||
// may cache them and replay via a raw write.
|
||||
func AppendPacket(dst []byte, threshold int32, id int32, body []byte) []byte {
|
||||
if threshold < 0 {
|
||||
return appendUncompressed(dst, id, body)
|
||||
}
|
||||
return appendCompressed(dst, threshold, id, body)
|
||||
}
|
||||
|
||||
func appendUncompressed(dst []byte, id int32, body []byte) []byte {
|
||||
total := VarIntLen(id) + len(body)
|
||||
dst = AppendVarInt(dst, int32(total))
|
||||
dst = AppendVarInt(dst, id)
|
||||
return append(dst, body...)
|
||||
}
|
||||
|
||||
func appendCompressed(dst []byte, threshold int32, id int32, body []byte) []byte {
|
||||
// raw = packet ID + body, the unit that compression applies to.
|
||||
raw := make([]byte, 0, VarIntLen(id)+len(body))
|
||||
raw = AppendVarInt(raw, id)
|
||||
raw = append(raw, body...)
|
||||
|
||||
var payload []byte
|
||||
if len(raw) >= int(threshold) {
|
||||
var buf bytes.Buffer
|
||||
zw := zlib.NewWriter(&buf)
|
||||
zw.Write(raw)
|
||||
zw.Close()
|
||||
// Data Length = uncompressed size, then the compressed bytes.
|
||||
payload = AppendVarInt(make([]byte, 0, VarIntLen(int32(len(raw)))+buf.Len()), int32(len(raw)))
|
||||
payload = append(payload, buf.Bytes()...)
|
||||
} else {
|
||||
// Below threshold: Data Length = 0, raw stored verbatim.
|
||||
payload = AppendVarInt(make([]byte, 0, 1+len(raw)), 0)
|
||||
payload = append(payload, raw...)
|
||||
}
|
||||
|
||||
dst = AppendVarInt(dst, int32(len(payload)))
|
||||
return append(dst, payload...)
|
||||
}
|
||||
114
internal/protocol/ids.go
Normal file
114
internal/protocol/ids.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
package protocol
|
||||
|
||||
// Packet IDs, grouped by state and direction. Serverbound = client→server,
|
||||
// Clientbound = server→client. Values are for protocol 775 (26.1.2).
|
||||
|
||||
// Handshaking, serverbound.
|
||||
const (
|
||||
HandshakeID = 0x00
|
||||
)
|
||||
|
||||
// Status, serverbound.
|
||||
const (
|
||||
StatusRequestID = 0x00
|
||||
PingRequestID = 0x01
|
||||
)
|
||||
|
||||
// Status, clientbound.
|
||||
const (
|
||||
StatusResponseID = 0x00
|
||||
PongResponseID = 0x01
|
||||
)
|
||||
|
||||
// Login, serverbound.
|
||||
const (
|
||||
LoginStartID = 0x00
|
||||
EncryptionResponse = 0x01
|
||||
LoginPluginResponse = 0x02
|
||||
LoginAcknowledgedID = 0x03
|
||||
CookieResponseLogin = 0x04
|
||||
)
|
||||
|
||||
// Login, clientbound.
|
||||
const (
|
||||
LoginDisconnectID = 0x00
|
||||
EncryptionRequest = 0x01
|
||||
LoginSuccessID = 0x02
|
||||
SetCompressionID = 0x03
|
||||
LoginPluginReq = 0x04
|
||||
CookieRequestLogin = 0x05
|
||||
)
|
||||
|
||||
// Configuration, serverbound.
|
||||
const (
|
||||
ConfigClientInformation = 0x00
|
||||
ConfigCookieResponse = 0x01
|
||||
ConfigCustomPayload = 0x02
|
||||
ConfigFinishServerbound = 0x03
|
||||
ConfigKeepAliveServer = 0x04
|
||||
ConfigPong = 0x05
|
||||
ConfigResourcePackResp = 0x06
|
||||
ConfigKnownPacksServer = 0x07
|
||||
)
|
||||
|
||||
// Configuration, clientbound.
|
||||
const (
|
||||
ConfigCookieRequest = 0x00
|
||||
ConfigCustomPayloadCB = 0x01
|
||||
ConfigDisconnect = 0x02
|
||||
ConfigFinishClientbound = 0x03
|
||||
ConfigKeepAliveCB = 0x04
|
||||
ConfigPing = 0x05
|
||||
ConfigRegistryData = 0x07
|
||||
ConfigUpdateEnabledFeatures = 0x0c
|
||||
ConfigUpdateTags = 0x0d
|
||||
ConfigKnownPacksCB = 0x0e
|
||||
)
|
||||
|
||||
// Play, clientbound (protocol 775).
|
||||
const (
|
||||
PlayLogin = 0x31
|
||||
PlayGameEvent = 0x26
|
||||
PlayKeepAliveCB = 0x2c
|
||||
PlayPlayerPosition = 0x48
|
||||
PlayDefaultSpawnPos = 0x61
|
||||
PlayChunkCacheCenter = 0x5e
|
||||
PlayLevelChunk = 0x2d
|
||||
PlayAbilities = 0x40
|
||||
PlaySetHeldSlot = 0x69
|
||||
PlayDisconnect = 0x20
|
||||
PlayBlockUpdate = 0x08
|
||||
PlayBlockChangedAck = 0x04
|
||||
PlaySystemChat = 0x79
|
||||
)
|
||||
|
||||
// Play, serverbound (protocol 775).
|
||||
const (
|
||||
PlayAcceptTeleport = 0x00
|
||||
PlayKeepAliveServer = 0x1c
|
||||
PlayClientTickEnd = 0x0d
|
||||
PlayClientInformation = 0x0e
|
||||
PlayCustomPayload = 0x16
|
||||
PlayMovePos = 0x1e
|
||||
PlayMovePosRot = 0x1f
|
||||
PlayMoveRot = 0x20
|
||||
PlayMoveStatusOnly = 0x21
|
||||
PlayPlayerLoaded = 0x2c
|
||||
PlayPlayerAction = 0x29
|
||||
PlayUseItemOn = 0x42
|
||||
PlaySetCreativeSlot = 0x38
|
||||
PlaySetCarriedItem = 0x35
|
||||
PlayChatMessage = 0x09
|
||||
)
|
||||
|
||||
// GameEvent sub-IDs carried by the clientbound game_event packet.
|
||||
const (
|
||||
GameEventStartWaitingChunks = 13
|
||||
)
|
||||
|
||||
// NextState values carried by the handshake packet.
|
||||
const (
|
||||
NextStateStatus = 1
|
||||
NextStateLogin = 2
|
||||
NextStateTransfer = 3
|
||||
)
|
||||
137
internal/protocol/types.go
Normal file
137
internal/protocol/types.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// Package protocol implements the wire-level primitives of the Minecraft
|
||||
// Java Edition protocol (version 26.1.2, protocol 775).
|
||||
//
|
||||
// All multi-byte numeric fields are big-endian. Length-prefixed and
|
||||
// frequently-used integers use the LEB128-style VarInt/VarLong encoding.
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Protocol constants for the targeted Minecraft version.
|
||||
const (
|
||||
// ProtocolVersion is the handshake protocol number for 26.1.2.
|
||||
ProtocolVersion = 775
|
||||
// GameVersion is the human-readable version string.
|
||||
GameVersion = "26.1.2"
|
||||
)
|
||||
|
||||
// State is a connection state as negotiated during the handshake. The numeric
|
||||
// values of Status/Login match the "next state" field of the handshake packet.
|
||||
type State int
|
||||
|
||||
const (
|
||||
StateHandshaking State = iota
|
||||
StateStatus
|
||||
StateLogin
|
||||
StateConfiguration
|
||||
StatePlay
|
||||
)
|
||||
|
||||
func (s State) String() string {
|
||||
switch s {
|
||||
case StateHandshaking:
|
||||
return "handshaking"
|
||||
case StateStatus:
|
||||
return "status"
|
||||
case StateLogin:
|
||||
return "login"
|
||||
case StateConfiguration:
|
||||
return "configuration"
|
||||
case StatePlay:
|
||||
return "play"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Protocol limits guarding against malicious or malformed input.
|
||||
const (
|
||||
// MaxVarIntLen is the maximum number of bytes a 32-bit VarInt may occupy.
|
||||
MaxVarIntLen = 5
|
||||
// MaxVarLongLen is the maximum number of bytes a 64-bit VarLong may occupy.
|
||||
MaxVarLongLen = 10
|
||||
// MaxStringLen bounds decoded strings to the protocol's 32767-char limit.
|
||||
MaxStringLen = 32767
|
||||
// MaxPacketSize bounds a single uncompressed packet body.
|
||||
MaxPacketSize = 2 * 1024 * 1024
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrVarIntTooBig is returned when a VarInt/VarLong exceeds its byte limit.
|
||||
ErrVarIntTooBig = errors.New("protocol: varint is too big")
|
||||
// ErrStringTooLong is returned when a string exceeds MaxStringLen.
|
||||
ErrStringTooLong = errors.New("protocol: string too long")
|
||||
// ErrPacketTooLarge is returned when a packet length exceeds MaxPacketSize.
|
||||
ErrPacketTooLarge = errors.New("protocol: packet too large")
|
||||
)
|
||||
|
||||
// ReadVarInt reads a 32-bit VarInt from r, returning the value and the number
|
||||
// of bytes consumed.
|
||||
func ReadVarInt(r io.ByteReader) (value int32, n int, err error) {
|
||||
var result uint32
|
||||
for i := 0; i < MaxVarIntLen; i++ {
|
||||
b, e := r.ReadByte()
|
||||
if e != nil {
|
||||
return 0, i, e
|
||||
}
|
||||
result |= uint32(b&0x7F) << (7 * i)
|
||||
if b&0x80 == 0 {
|
||||
return int32(result), i + 1, nil
|
||||
}
|
||||
}
|
||||
return 0, MaxVarIntLen, ErrVarIntTooBig
|
||||
}
|
||||
|
||||
// ReadVarLong reads a 64-bit VarLong from r.
|
||||
func ReadVarLong(r io.ByteReader) (value int64, n int, err error) {
|
||||
var result uint64
|
||||
for i := 0; i < MaxVarLongLen; i++ {
|
||||
b, e := r.ReadByte()
|
||||
if e != nil {
|
||||
return 0, i, e
|
||||
}
|
||||
result |= uint64(b&0x7F) << (7 * i)
|
||||
if b&0x80 == 0 {
|
||||
return int64(result), i + 1, nil
|
||||
}
|
||||
}
|
||||
return 0, MaxVarLongLen, ErrVarIntTooBig
|
||||
}
|
||||
|
||||
// AppendVarInt encodes v as a VarInt and appends it to dst.
|
||||
func AppendVarInt(dst []byte, v int32) []byte {
|
||||
u := uint32(v)
|
||||
for {
|
||||
if u&^0x7F == 0 {
|
||||
return append(dst, byte(u))
|
||||
}
|
||||
dst = append(dst, byte(u&0x7F)|0x80)
|
||||
u >>= 7
|
||||
}
|
||||
}
|
||||
|
||||
// AppendVarLong encodes v as a VarLong and appends it to dst.
|
||||
func AppendVarLong(dst []byte, v int64) []byte {
|
||||
u := uint64(v)
|
||||
for {
|
||||
if u&^uint64(0x7F) == 0 {
|
||||
return append(dst, byte(u))
|
||||
}
|
||||
dst = append(dst, byte(u&0x7F)|0x80)
|
||||
u >>= 7
|
||||
}
|
||||
}
|
||||
|
||||
// VarIntLen returns the number of bytes the VarInt encoding of v occupies.
|
||||
func VarIntLen(v int32) int {
|
||||
u := uint32(v)
|
||||
n := 1
|
||||
for u&^0x7F != 0 {
|
||||
u >>= 7
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue