| // 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" |
| |
| "gvisor.dev/gvisor/pkg/gohacks" |
| ) |
| |
| // Reader is the required reader interface. |
| type Reader interface { |
| io.Reader |
| ReadByte() (byte, error) |
| } |
| |
| // Writer is the required writer interface. |
| type Writer interface { |
| io.Writer |
| WriteByte(byte) error |
| } |
| |
| // 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 io.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, err := r.ReadByte() |
| if err != nil { |
| panic(err) |
| } |
| 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) { |
| for u >= 0x80 { |
| if err := w.WriteByte(byte(u) | 0x80); err != nil { |
| panic(err) |
| } |
| u >>= 7 |
| } |
| if err := w.WriteByte(byte(u)); 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) |
| } |