Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для неё не нужна. Module path github.com/sagernet/gvisor сохранён намеренно: на него опирается replace-директива суперпроекта. Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show и переносилась на новый пин копированием. SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
983 lines
20 KiB
Go
983 lines
20 KiB
Go
// Copyright 2020 The gVisor 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.
|
|
|
|
// Package wire contains a few basic types that can be composed to serialize
|
|
// graph information for the state package. This package defines the wire
|
|
// protocol.
|
|
//
|
|
// Note that these types are careful about how they implement the relevant
|
|
// interfaces (either value receiver or pointer receiver), so that native-sized
|
|
// types, such as integers and simple pointers, can fit inside the interface
|
|
// object.
|
|
//
|
|
// This package also uses panic as control flow, so called should be careful to
|
|
// wrap calls in appropriate handlers.
|
|
//
|
|
// Testing for this package is driven by the state test package.
|
|
package wire
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
|
|
"github.com/sagernet/gvisor/pkg/gohacks"
|
|
)
|
|
|
|
// Reader bundles an io.Reader with a buffer used to implement readByte
|
|
// efficiently.
|
|
type Reader struct {
|
|
io.Reader
|
|
|
|
buf [1]byte
|
|
}
|
|
|
|
// readByte reads a single byte from r.Reader without allocation. It panics on
|
|
// error.
|
|
func (r *Reader) readByte() byte {
|
|
n, err := r.Read(r.buf[:])
|
|
if n != 1 {
|
|
panic(err)
|
|
}
|
|
return r.buf[0]
|
|
}
|
|
|
|
// Writer bundles an io.Writer with a buffer used to implement writeByte
|
|
// efficiently.
|
|
type Writer struct {
|
|
io.Writer
|
|
|
|
// buf is used by Uint as a scratch buffer.
|
|
buf [10]byte
|
|
}
|
|
|
|
// readFull is a utility. The equivalent is not needed for Write, but the API
|
|
// contract dictates that it must always complete all bytes given or return an
|
|
// error.
|
|
func readFull(r *Reader, p []byte) {
|
|
for done := 0; done < len(p); {
|
|
n, err := r.Read(p[done:])
|
|
done += n
|
|
if n == 0 && err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Object is a generic object.
|
|
type Object interface {
|
|
// save saves the given object.
|
|
//
|
|
// Panic is used for error control flow.
|
|
save(*Writer)
|
|
|
|
// load loads a new object of the given type.
|
|
//
|
|
// Panic is used for error control flow.
|
|
load(*Reader) Object
|
|
}
|
|
|
|
// Bool is a boolean.
|
|
type Bool bool
|
|
|
|
// loadBool loads an object of type Bool.
|
|
func loadBool(r *Reader) Bool {
|
|
b := loadUint(r)
|
|
return Bool(b == 1)
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (b Bool) save(w *Writer) {
|
|
var v Uint
|
|
if b {
|
|
v = 1
|
|
} else {
|
|
v = 0
|
|
}
|
|
v.save(w)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (Bool) load(r *Reader) Object { return loadBool(r) }
|
|
|
|
// Int is a signed integer.
|
|
//
|
|
// This uses varint encoding.
|
|
type Int int64
|
|
|
|
// loadInt loads an object of type Int.
|
|
func loadInt(r *Reader) Int {
|
|
u := loadUint(r)
|
|
x := Int(u >> 1)
|
|
if u&1 != 0 {
|
|
x = ^x
|
|
}
|
|
return x
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (i Int) save(w *Writer) {
|
|
u := Uint(i) << 1
|
|
if i < 0 {
|
|
u = ^u
|
|
}
|
|
u.save(w)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (Int) load(r *Reader) Object { return loadInt(r) }
|
|
|
|
// Uint is an unsigned integer.
|
|
type Uint uint64
|
|
|
|
// loadUint loads an object of type Uint.
|
|
func loadUint(r *Reader) Uint {
|
|
var (
|
|
u Uint
|
|
s uint
|
|
)
|
|
for i := 0; i <= 9; i++ {
|
|
b := r.readByte()
|
|
if b < 0x80 {
|
|
if i == 9 && b > 1 {
|
|
panic("overflow")
|
|
}
|
|
u |= Uint(b) << s
|
|
return u
|
|
}
|
|
u |= Uint(b&0x7f) << s
|
|
s += 7
|
|
}
|
|
panic("unreachable")
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (u Uint) save(w *Writer) {
|
|
i := 0
|
|
for u >= 0x80 {
|
|
w.buf[i] = byte(u) | 0x80
|
|
i++
|
|
u >>= 7
|
|
}
|
|
w.buf[i] = byte(u)
|
|
if _, err := w.Write(w.buf[:i+1]); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (Uint) load(r *Reader) Object { return loadUint(r) }
|
|
|
|
// Float32 is a 32-bit floating point number.
|
|
type Float32 float32
|
|
|
|
// loadFloat32 loads an object of type Float32.
|
|
func loadFloat32(r *Reader) Float32 {
|
|
n := loadUint(r)
|
|
return Float32(math.Float32frombits(uint32(n)))
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (f Float32) save(w *Writer) {
|
|
n := Uint(math.Float32bits(float32(f)))
|
|
n.save(w)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (Float32) load(r *Reader) Object { return loadFloat32(r) }
|
|
|
|
// Float64 is a 64-bit floating point number.
|
|
type Float64 float64
|
|
|
|
// loadFloat64 loads an object of type Float64.
|
|
func loadFloat64(r *Reader) Float64 {
|
|
n := loadUint(r)
|
|
return Float64(math.Float64frombits(uint64(n)))
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (f Float64) save(w *Writer) {
|
|
n := Uint(math.Float64bits(float64(f)))
|
|
n.save(w)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (Float64) load(r *Reader) Object { return loadFloat64(r) }
|
|
|
|
// Complex64 is a 64-bit complex number.
|
|
type Complex64 complex128
|
|
|
|
// loadComplex64 loads an object of type Complex64.
|
|
func loadComplex64(r *Reader) Complex64 {
|
|
re := loadFloat32(r)
|
|
im := loadFloat32(r)
|
|
return Complex64(complex(float32(re), float32(im)))
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (c *Complex64) save(w *Writer) {
|
|
re := Float32(real(*c))
|
|
im := Float32(imag(*c))
|
|
re.save(w)
|
|
im.save(w)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Complex64) load(r *Reader) Object {
|
|
c := loadComplex64(r)
|
|
return &c
|
|
}
|
|
|
|
// Complex128 is a 128-bit complex number.
|
|
type Complex128 complex128
|
|
|
|
// loadComplex128 loads an object of type Complex128.
|
|
func loadComplex128(r *Reader) Complex128 {
|
|
re := loadFloat64(r)
|
|
im := loadFloat64(r)
|
|
return Complex128(complex(float64(re), float64(im)))
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (c *Complex128) save(w *Writer) {
|
|
re := Float64(real(*c))
|
|
im := Float64(imag(*c))
|
|
re.save(w)
|
|
im.save(w)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Complex128) load(r *Reader) Object {
|
|
c := loadComplex128(r)
|
|
return &c
|
|
}
|
|
|
|
// String is a string.
|
|
type String string
|
|
|
|
// loadString loads an object of type String.
|
|
func loadString(r *Reader) String {
|
|
l := loadUint(r)
|
|
p := make([]byte, l)
|
|
readFull(r, p)
|
|
return String(gohacks.StringFromImmutableBytes(p))
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (s *String) save(w *Writer) {
|
|
l := Uint(len(*s))
|
|
l.save(w)
|
|
p := gohacks.ImmutableBytesFromString(string(*s))
|
|
_, err := w.Write(p) // Must write all bytes.
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*String) load(r *Reader) Object {
|
|
s := loadString(r)
|
|
return &s
|
|
}
|
|
|
|
// Dot is a kind of reference: one of Index and FieldName.
|
|
type Dot interface {
|
|
isDot()
|
|
}
|
|
|
|
// Index is a reference resolution.
|
|
type Index uint32
|
|
|
|
func (Index) isDot() {}
|
|
|
|
// FieldName is a reference resolution.
|
|
type FieldName string
|
|
|
|
func (*FieldName) isDot() {}
|
|
|
|
// Ref is a reference to an object.
|
|
type Ref struct {
|
|
// Root is the root object.
|
|
Root Uint
|
|
|
|
// Dots is the set of traversals required from the Root object above.
|
|
// Note that this will be stored in reverse order for efficiency.
|
|
Dots []Dot
|
|
|
|
// Type is the base type for the root object. This is non-nil iff Dots
|
|
// is non-zero length (that is, this is a complex reference). This is
|
|
// not *strictly* necessary, but can be used to simplify decoding.
|
|
Type TypeSpec
|
|
}
|
|
|
|
// loadRef loads an object of type Ref (abstract).
|
|
func loadRef(r *Reader) Ref {
|
|
ref := Ref{
|
|
Root: loadUint(r),
|
|
}
|
|
l := loadUint(r)
|
|
ref.Dots = make([]Dot, l)
|
|
for i := 0; i < int(l); i++ {
|
|
// Disambiguate between an Index (non-negative) and a field
|
|
// name (negative). This does some space and avoids a dedicate
|
|
// loadDot function. See Ref.save for the other side.
|
|
d := loadInt(r)
|
|
if d >= 0 {
|
|
ref.Dots[i] = Index(d)
|
|
continue
|
|
}
|
|
p := make([]byte, -d)
|
|
readFull(r, p)
|
|
fieldName := FieldName(gohacks.StringFromImmutableBytes(p))
|
|
ref.Dots[i] = &fieldName
|
|
}
|
|
if l != 0 {
|
|
// Only if dots is non-zero.
|
|
ref.Type = loadTypeSpec(r)
|
|
}
|
|
return ref
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (r *Ref) save(w *Writer) {
|
|
r.Root.save(w)
|
|
l := Uint(len(r.Dots))
|
|
l.save(w)
|
|
for _, d := range r.Dots {
|
|
// See LoadRef. We use non-negative numbers to encode Index
|
|
// objects and negative numbers to encode field lengths.
|
|
switch x := d.(type) {
|
|
case Index:
|
|
i := Int(x)
|
|
i.save(w)
|
|
case *FieldName:
|
|
d := Int(-len(*x))
|
|
d.save(w)
|
|
p := gohacks.ImmutableBytesFromString(string(*x))
|
|
if _, err := w.Write(p); err != nil {
|
|
panic(err)
|
|
}
|
|
default:
|
|
panic("unknown dot implementation")
|
|
}
|
|
}
|
|
if l != 0 {
|
|
// See above.
|
|
saveTypeSpec(w, r.Type)
|
|
}
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Ref) load(r *Reader) Object {
|
|
ref := loadRef(r)
|
|
return &ref
|
|
}
|
|
|
|
// Nil is a primitive zero value of any type.
|
|
type Nil struct{}
|
|
|
|
// loadNil loads an object of type Nil.
|
|
func loadNil(r *Reader) Nil {
|
|
return Nil{}
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (Nil) save(w *Writer) {}
|
|
|
|
// load implements Object.load.
|
|
func (Nil) load(r *Reader) Object { return loadNil(r) }
|
|
|
|
// Slice is a slice value.
|
|
type Slice struct {
|
|
Length Uint
|
|
Capacity Uint
|
|
Ref Ref
|
|
}
|
|
|
|
// loadSlice loads an object of type Slice.
|
|
func loadSlice(r *Reader) Slice {
|
|
return Slice{
|
|
Length: loadUint(r),
|
|
Capacity: loadUint(r),
|
|
Ref: loadRef(r),
|
|
}
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (s *Slice) save(w *Writer) {
|
|
s.Length.save(w)
|
|
s.Capacity.save(w)
|
|
s.Ref.save(w)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Slice) load(r *Reader) Object {
|
|
s := loadSlice(r)
|
|
return &s
|
|
}
|
|
|
|
// Array is an array value.
|
|
type Array struct {
|
|
Contents []Object
|
|
}
|
|
|
|
// loadArray loads an object of type Array.
|
|
func loadArray(r *Reader) Array {
|
|
l := loadUint(r)
|
|
if l == 0 {
|
|
// Note that there isn't a single object available to encode
|
|
// the type of, so we need this additional branch.
|
|
return Array{}
|
|
}
|
|
// All the objects here have the same type, so use dynamic dispatch
|
|
// only once. All other objects will automatically take the same type
|
|
// as the first object.
|
|
contents := make([]Object, l)
|
|
v := Load(r)
|
|
contents[0] = v
|
|
for i := 1; i < int(l); i++ {
|
|
contents[i] = v.load(r)
|
|
}
|
|
return Array{
|
|
Contents: contents,
|
|
}
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (a *Array) save(w *Writer) {
|
|
l := Uint(len(a.Contents))
|
|
l.save(w)
|
|
if l == 0 {
|
|
// See LoadArray.
|
|
return
|
|
}
|
|
// See above.
|
|
Save(w, a.Contents[0])
|
|
for i := 1; i < int(l); i++ {
|
|
a.Contents[i].save(w)
|
|
}
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Array) load(r *Reader) Object {
|
|
a := loadArray(r)
|
|
return &a
|
|
}
|
|
|
|
// Map is a map value.
|
|
type Map struct {
|
|
Keys []Object
|
|
Values []Object
|
|
}
|
|
|
|
// loadMap loads an object of type Map.
|
|
func loadMap(r *Reader) Map {
|
|
l := loadUint(r)
|
|
if l == 0 {
|
|
// See LoadArray.
|
|
return Map{}
|
|
}
|
|
// See type dispatch notes in Array.
|
|
keys := make([]Object, l)
|
|
values := make([]Object, l)
|
|
k := Load(r)
|
|
v := Load(r)
|
|
keys[0] = k
|
|
values[0] = v
|
|
for i := 1; i < int(l); i++ {
|
|
keys[i] = k.load(r)
|
|
values[i] = v.load(r)
|
|
}
|
|
return Map{
|
|
Keys: keys,
|
|
Values: values,
|
|
}
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (m *Map) save(w *Writer) {
|
|
l := Uint(len(m.Keys))
|
|
if int(l) != len(m.Values) {
|
|
panic(fmt.Sprintf("mismatched keys (%d) Aand values (%d)", len(m.Keys), len(m.Values)))
|
|
}
|
|
l.save(w)
|
|
if l == 0 {
|
|
// See LoadArray.
|
|
return
|
|
}
|
|
// See above.
|
|
Save(w, m.Keys[0])
|
|
Save(w, m.Values[0])
|
|
for i := 1; i < int(l); i++ {
|
|
m.Keys[i].save(w)
|
|
m.Values[i].save(w)
|
|
}
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Map) load(r *Reader) Object {
|
|
m := loadMap(r)
|
|
return &m
|
|
}
|
|
|
|
// TypeSpec is a type dereference.
|
|
type TypeSpec interface {
|
|
isTypeSpec()
|
|
}
|
|
|
|
// TypeID is a concrete type ID.
|
|
type TypeID Uint
|
|
|
|
func (TypeID) isTypeSpec() {}
|
|
|
|
// TypeSpecPointer is a pointer type.
|
|
type TypeSpecPointer struct {
|
|
Type TypeSpec
|
|
}
|
|
|
|
func (*TypeSpecPointer) isTypeSpec() {}
|
|
|
|
// TypeSpecArray is an array type.
|
|
type TypeSpecArray struct {
|
|
Count Uint
|
|
Type TypeSpec
|
|
}
|
|
|
|
func (*TypeSpecArray) isTypeSpec() {}
|
|
|
|
// TypeSpecSlice is a slice type.
|
|
type TypeSpecSlice struct {
|
|
Type TypeSpec
|
|
}
|
|
|
|
func (*TypeSpecSlice) isTypeSpec() {}
|
|
|
|
// TypeSpecMap is a map type.
|
|
type TypeSpecMap struct {
|
|
Key TypeSpec
|
|
Value TypeSpec
|
|
}
|
|
|
|
func (*TypeSpecMap) isTypeSpec() {}
|
|
|
|
// TypeSpecNil is an empty type.
|
|
type TypeSpecNil struct{}
|
|
|
|
func (TypeSpecNil) isTypeSpec() {}
|
|
|
|
// TypeSpec types.
|
|
//
|
|
// These use a distinct encoding on the wire, as they are used only in the
|
|
// interface object. They are decoded through the dedicated loadTypeSpec and
|
|
// saveTypeSpec functions.
|
|
const (
|
|
typeSpecTypeID Uint = iota
|
|
typeSpecPointer
|
|
typeSpecArray
|
|
typeSpecSlice
|
|
typeSpecMap
|
|
typeSpecNil
|
|
)
|
|
|
|
// loadTypeSpec loads TypeSpec values.
|
|
func loadTypeSpec(r *Reader) TypeSpec {
|
|
switch hdr := loadUint(r); hdr {
|
|
case typeSpecTypeID:
|
|
return TypeID(loadUint(r))
|
|
case typeSpecPointer:
|
|
return &TypeSpecPointer{
|
|
Type: loadTypeSpec(r),
|
|
}
|
|
case typeSpecArray:
|
|
return &TypeSpecArray{
|
|
Count: loadUint(r),
|
|
Type: loadTypeSpec(r),
|
|
}
|
|
case typeSpecSlice:
|
|
return &TypeSpecSlice{
|
|
Type: loadTypeSpec(r),
|
|
}
|
|
case typeSpecMap:
|
|
return &TypeSpecMap{
|
|
Key: loadTypeSpec(r),
|
|
Value: loadTypeSpec(r),
|
|
}
|
|
case typeSpecNil:
|
|
return TypeSpecNil{}
|
|
default:
|
|
// This is not a valid stream?
|
|
panic(fmt.Errorf("unknown header: %d", hdr))
|
|
}
|
|
}
|
|
|
|
// saveTypeSpec saves TypeSpec values.
|
|
func saveTypeSpec(w *Writer, t TypeSpec) {
|
|
switch x := t.(type) {
|
|
case TypeID:
|
|
typeSpecTypeID.save(w)
|
|
Uint(x).save(w)
|
|
case *TypeSpecPointer:
|
|
typeSpecPointer.save(w)
|
|
saveTypeSpec(w, x.Type)
|
|
case *TypeSpecArray:
|
|
typeSpecArray.save(w)
|
|
x.Count.save(w)
|
|
saveTypeSpec(w, x.Type)
|
|
case *TypeSpecSlice:
|
|
typeSpecSlice.save(w)
|
|
saveTypeSpec(w, x.Type)
|
|
case *TypeSpecMap:
|
|
typeSpecMap.save(w)
|
|
saveTypeSpec(w, x.Key)
|
|
saveTypeSpec(w, x.Value)
|
|
case TypeSpecNil:
|
|
typeSpecNil.save(w)
|
|
default:
|
|
// This should not happen?
|
|
panic(fmt.Errorf("unknown type %T", t))
|
|
}
|
|
}
|
|
|
|
// Interface is an interface value.
|
|
type Interface struct {
|
|
Type TypeSpec
|
|
Value Object
|
|
}
|
|
|
|
// loadInterface loads an object of type Interface.
|
|
func loadInterface(r *Reader) Interface {
|
|
return Interface{
|
|
Type: loadTypeSpec(r),
|
|
Value: Load(r),
|
|
}
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (i *Interface) save(w *Writer) {
|
|
saveTypeSpec(w, i.Type)
|
|
Save(w, i.Value)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Interface) load(r *Reader) Object {
|
|
i := loadInterface(r)
|
|
return &i
|
|
}
|
|
|
|
// Type is type information.
|
|
type Type struct {
|
|
Name string
|
|
Fields []string
|
|
}
|
|
|
|
// loadType loads an object of type Type.
|
|
func loadType(r *Reader) Type {
|
|
name := string(loadString(r))
|
|
l := loadUint(r)
|
|
fields := make([]string, l)
|
|
for i := 0; i < int(l); i++ {
|
|
fields[i] = string(loadString(r))
|
|
}
|
|
return Type{
|
|
Name: name,
|
|
Fields: fields,
|
|
}
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (t *Type) save(w *Writer) {
|
|
s := String(t.Name)
|
|
s.save(w)
|
|
l := Uint(len(t.Fields))
|
|
l.save(w)
|
|
for i := 0; i < int(l); i++ {
|
|
s := String(t.Fields[i])
|
|
s.save(w)
|
|
}
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Type) load(r *Reader) Object {
|
|
t := loadType(r)
|
|
return &t
|
|
}
|
|
|
|
// multipleObjects is a special type for serializing multiple objects.
|
|
type multipleObjects []Object
|
|
|
|
// loadMultipleObjects loads a series of objects.
|
|
func loadMultipleObjects(r *Reader) multipleObjects {
|
|
l := loadUint(r)
|
|
m := make(multipleObjects, l)
|
|
for i := 0; i < int(l); i++ {
|
|
m[i] = Load(r)
|
|
}
|
|
return m
|
|
}
|
|
|
|
// save implements Object.save.
|
|
func (m *multipleObjects) save(w *Writer) {
|
|
l := Uint(len(*m))
|
|
l.save(w)
|
|
for i := 0; i < int(l); i++ {
|
|
Save(w, (*m)[i])
|
|
}
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*multipleObjects) load(r *Reader) Object {
|
|
m := loadMultipleObjects(r)
|
|
return &m
|
|
}
|
|
|
|
// noObjects represents no objects.
|
|
type noObjects struct{}
|
|
|
|
// loadNoObjects loads a sentinel.
|
|
func loadNoObjects(r *Reader) noObjects { return noObjects{} }
|
|
|
|
// save implements Object.save.
|
|
func (noObjects) save(w *Writer) {}
|
|
|
|
// load implements Object.load.
|
|
func (noObjects) load(r *Reader) Object { return loadNoObjects(r) }
|
|
|
|
// Struct is a basic composite value.
|
|
type Struct struct {
|
|
TypeID TypeID
|
|
fields Object // Optionally noObjects or *multipleObjects.
|
|
}
|
|
|
|
// Field returns a pointer to the given field slot.
|
|
//
|
|
// This must be called after Alloc.
|
|
func (s *Struct) Field(i int) *Object {
|
|
if fields, ok := s.fields.(*multipleObjects); ok {
|
|
return &((*fields)[i])
|
|
}
|
|
if _, ok := s.fields.(noObjects); ok {
|
|
// Alloc may be optionally called; can't call twice.
|
|
panic("Field called inappropriately, wrong Alloc?")
|
|
}
|
|
return &s.fields
|
|
}
|
|
|
|
// Alloc allocates the given number of fields.
|
|
//
|
|
// This must be called before Add and Save.
|
|
//
|
|
// Precondition: slots must be positive.
|
|
func (s *Struct) Alloc(slots int) {
|
|
switch {
|
|
case slots == 0:
|
|
s.fields = noObjects{}
|
|
case slots == 1:
|
|
// Leave it alone.
|
|
case slots > 1:
|
|
fields := make(multipleObjects, slots)
|
|
s.fields = &fields
|
|
default:
|
|
// Violates precondition.
|
|
panic(fmt.Sprintf("Alloc called with negative slots %d?", slots))
|
|
}
|
|
}
|
|
|
|
// Fields returns the number of fields.
|
|
func (s *Struct) Fields() int {
|
|
switch x := s.fields.(type) {
|
|
case *multipleObjects:
|
|
return len(*x)
|
|
case noObjects:
|
|
return 0
|
|
default:
|
|
return 1
|
|
}
|
|
}
|
|
|
|
// loadStruct loads an object of type Struct.
|
|
func loadStruct(r *Reader) Struct {
|
|
return Struct{
|
|
TypeID: TypeID(loadUint(r)),
|
|
fields: Load(r),
|
|
}
|
|
}
|
|
|
|
// save implements Object.save.
|
|
//
|
|
// Precondition: Alloc must have been called, and the fields all filled in
|
|
// appropriately. See Alloc and Add for more details.
|
|
func (s *Struct) save(w *Writer) {
|
|
Uint(s.TypeID).save(w)
|
|
Save(w, s.fields)
|
|
}
|
|
|
|
// load implements Object.load.
|
|
func (*Struct) load(r *Reader) Object {
|
|
s := loadStruct(r)
|
|
return &s
|
|
}
|
|
|
|
// Object types.
|
|
//
|
|
// N.B. Be careful about changing the order or introducing new elements in the
|
|
// middle here. This is part of the wire format and shouldn't change.
|
|
const (
|
|
typeBool Uint = iota
|
|
typeInt
|
|
typeUint
|
|
typeFloat32
|
|
typeFloat64
|
|
typeNil
|
|
typeRef
|
|
typeString
|
|
typeSlice
|
|
typeArray
|
|
typeMap
|
|
typeStruct
|
|
typeNoObjects
|
|
typeMultipleObjects
|
|
typeInterface
|
|
typeComplex64
|
|
typeComplex128
|
|
typeType
|
|
)
|
|
|
|
// Save saves the given object.
|
|
//
|
|
// +checkescape all
|
|
//
|
|
// N.B. This function will panic on error.
|
|
func Save(w *Writer, obj Object) {
|
|
switch x := obj.(type) {
|
|
case Bool:
|
|
typeBool.save(w)
|
|
x.save(w)
|
|
case Int:
|
|
typeInt.save(w)
|
|
x.save(w)
|
|
case Uint:
|
|
typeUint.save(w)
|
|
x.save(w)
|
|
case Float32:
|
|
typeFloat32.save(w)
|
|
x.save(w)
|
|
case Float64:
|
|
typeFloat64.save(w)
|
|
x.save(w)
|
|
case Nil:
|
|
typeNil.save(w)
|
|
x.save(w)
|
|
case *Ref:
|
|
typeRef.save(w)
|
|
x.save(w)
|
|
case *String:
|
|
typeString.save(w)
|
|
x.save(w)
|
|
case *Slice:
|
|
typeSlice.save(w)
|
|
x.save(w)
|
|
case *Array:
|
|
typeArray.save(w)
|
|
x.save(w)
|
|
case *Map:
|
|
typeMap.save(w)
|
|
x.save(w)
|
|
case *Struct:
|
|
typeStruct.save(w)
|
|
x.save(w)
|
|
case noObjects:
|
|
typeNoObjects.save(w)
|
|
x.save(w)
|
|
case *multipleObjects:
|
|
typeMultipleObjects.save(w)
|
|
x.save(w)
|
|
case *Interface:
|
|
typeInterface.save(w)
|
|
x.save(w)
|
|
case *Type:
|
|
typeType.save(w)
|
|
x.save(w)
|
|
case *Complex64:
|
|
typeComplex64.save(w)
|
|
x.save(w)
|
|
case *Complex128:
|
|
typeComplex128.save(w)
|
|
x.save(w)
|
|
default:
|
|
panic(fmt.Errorf("unknown type: %#v", obj))
|
|
}
|
|
}
|
|
|
|
// Load loads a new object.
|
|
//
|
|
// +checkescape all
|
|
//
|
|
// N.B. This function will panic on error.
|
|
func Load(r *Reader) Object {
|
|
switch hdr := loadUint(r); hdr {
|
|
case typeBool:
|
|
return loadBool(r)
|
|
case typeInt:
|
|
return loadInt(r)
|
|
case typeUint:
|
|
return loadUint(r)
|
|
case typeFloat32:
|
|
return loadFloat32(r)
|
|
case typeFloat64:
|
|
return loadFloat64(r)
|
|
case typeNil:
|
|
return loadNil(r)
|
|
case typeRef:
|
|
return ((*Ref)(nil)).load(r) // Escapes.
|
|
case typeString:
|
|
return ((*String)(nil)).load(r) // Escapes.
|
|
case typeSlice:
|
|
return ((*Slice)(nil)).load(r) // Escapes.
|
|
case typeArray:
|
|
return ((*Array)(nil)).load(r) // Escapes.
|
|
case typeMap:
|
|
return ((*Map)(nil)).load(r) // Escapes.
|
|
case typeStruct:
|
|
return ((*Struct)(nil)).load(r) // Escapes.
|
|
case typeNoObjects: // Special for struct.
|
|
return loadNoObjects(r)
|
|
case typeMultipleObjects: // Special for struct.
|
|
return ((*multipleObjects)(nil)).load(r) // Escapes.
|
|
case typeInterface:
|
|
return ((*Interface)(nil)).load(r) // Escapes.
|
|
case typeComplex64:
|
|
return ((*Complex64)(nil)).load(r) // Escapes.
|
|
case typeComplex128:
|
|
return ((*Complex128)(nil)).load(r) // Escapes.
|
|
case typeType:
|
|
return ((*Type)(nil)).load(r) // Escapes.
|
|
default:
|
|
// This is not a valid stream?
|
|
panic(fmt.Errorf("unknown header: %d", hdr))
|
|
}
|
|
}
|
|
|
|
// LoadUint loads a single unsigned integer.
|
|
//
|
|
// N.B. This function will panic on error.
|
|
func LoadUint(r *Reader) uint64 {
|
|
return uint64(loadUint(r))
|
|
}
|
|
|
|
// SaveUint saves a single unsigned integer.
|
|
//
|
|
// N.B. This function will panic on error.
|
|
func SaveUint(w *Writer, v uint64) {
|
|
Uint(v).save(w)
|
|
}
|