// 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) }