// Copyright 2009 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package gobimport ()// An Encoder manages the transmission of type and data information to the// other side of a connection. It is safe for concurrent use by multiple// goroutines.typeEncoderstruct { mutex sync.Mutex// each item must be sent atomically w []io.Writer// where to send the data sent map[reflect.Type]typeId// which types we've already sent countState *encoderState// stage for writing counts freeList *encoderState// list of free encoderStates; avoids reallocation byteBuf encBuffer// buffer for top-level encoderState err error}// Before we encode a message, we reserve space at the head of the// buffer in which to encode its length. This means we can use the// buffer to assemble the message without another allocation.const maxLength = 9// Maximum size of an encoded length.var spaceForLength = make([]byte, maxLength)// NewEncoder returns a new encoder that will transmit on the [io.Writer].func ( io.Writer) *Encoder { := new(Encoder) .w = []io.Writer{} .sent = make(map[reflect.Type]typeId) .countState = .newEncoderState(new(encBuffer))return}// writer returns the innermost writer the encoder is using.func ( *Encoder) () io.Writer {return .w[len(.w)-1]}// pushWriter adds a writer to the encoder.func ( *Encoder) ( io.Writer) { .w = append(.w, )}// popWriter pops the innermost writer.func ( *Encoder) () { .w = .w[0 : len(.w)-1]}func ( *Encoder) ( error) {if .err == nil { // remember the first. .err = }}// writeMessage sends the data item preceded by an unsigned count of its length.func ( *Encoder) ( io.Writer, *encBuffer) {// Space has been reserved for the length at the head of the message. // This is a little dirty: we grab the slice from the bytes.Buffer and massage // it by hand. := .Bytes() := len() - maxLength// Length cannot be bigger than the decoder can handle.if >= tooBig { .setError(errors.New("gob: encoder: message too big"))return }// Encode the length. .countState.b.Reset() .countState.encodeUint(uint64())// Copy the length to be a prefix of the message. := maxLength - .countState.b.Len()copy([:], .countState.b.Bytes())// Write the data. , := .Write([:])// Drain the buffer and restore the space at the front for the count of the next message. .Reset() .Write(spaceForLength)if != nil { .setError() }}// sendActualType sends the requested type, without further investigation, unless// it's been sent before.func ( *Encoder) ( io.Writer, *encoderState, *userTypeInfo, reflect.Type) ( bool) {if , := .sent[]; {returnfalse } , := getTypeInfo()if != nil { .setError()return }// Send the pair (-id, type) // Id: .encodeInt(-int64(.id))// Type: .encode(.b, reflect.ValueOf(.wire), wireTypeUserInfo) .writeMessage(, .b)if .err != nil {return }// Remember we've sent this type, both what the user gave us and the base type. .sent[.base] = .idif .user != .base { .sent[.user] = .id }// Now send the inner typesswitch := ; .Kind() {casereflect.Struct:for := 0; < .NumField(); ++ {ifisExported(.Field().Name) { .sendType(, , .Field().Type) } }casereflect.Array, reflect.Slice: .sendType(, , .Elem())casereflect.Map: .sendType(, , .Key()) .sendType(, , .Elem()) }returntrue}// sendType sends the type info to the other side, if necessary.func ( *Encoder) ( io.Writer, *encoderState, reflect.Type) ( bool) { := userType()if .externalEnc != 0 {// The rules are different: regardless of the underlying type's representation, // we need to tell the other side that the base type is a GobEncoder.return .sendActualType(, , , .base) }// It's a concrete value, so drill down to the base type.switch := .base; .Kind() {default:// Basic types and interfaces do not need to be described.returncasereflect.Slice:// If it's []uint8, don't send; it's considered basic.if .Elem().Kind() == reflect.Uint8 {return }// Otherwise we do send.breakcasereflect.Array:// arrays must be sent so we know their lengths and element types.breakcasereflect.Map:// maps must be sent so we know their lengths and key/value types.breakcasereflect.Struct:// structs must be sent so we know their fields.breakcasereflect.Chan, reflect.Func:// If we get here, it's a field of a struct; ignore it.return }return .sendActualType(, , , .base)}// Encode transmits the data item represented by the empty interface value,// guaranteeing that all necessary type information has been transmitted first.// Passing a nil pointer to Encoder will panic, as they cannot be transmitted by gob.func ( *Encoder) ( any) error {return .EncodeValue(reflect.ValueOf())}// sendTypeDescriptor makes sure the remote side knows about this type.// It will send a descriptor if this is the first time the type has been// sent.func ( *Encoder) ( io.Writer, *encoderState, *userTypeInfo) {// Make sure the type is known to the other side. // First, have we already sent this type? := .baseif .externalEnc != 0 { = .user }if , := .sent[]; ! {// No, so send it. := .sendType(, , )if .err != nil {return }// If the type info has still not been transmitted, it means we have // a singleton basic type (int, []byte etc.) at top level. We don't // need to send the type info but we do need to update enc.sent.if ! { , := getTypeInfo()if != nil { .setError()return } .sent[] = .id } }}// sendTypeId sends the id, which must have already been defined.func ( *Encoder) ( *encoderState, *userTypeInfo) {// Identify the type of this top-level value. .encodeInt(int64(.sent[.base]))}// EncodeValue transmits the data item represented by the reflection value,// guaranteeing that all necessary type information has been transmitted first.// Passing a nil pointer to EncodeValue will panic, as they cannot be transmitted by gob.func ( *Encoder) ( reflect.Value) error {if .Kind() == reflect.Invalid {returnerrors.New("gob: cannot encode nil value") }if .Kind() == reflect.Pointer && .IsNil() {panic("gob: cannot encode nil pointer of type " + .Type().String()) }// Make sure we're single-threaded through here, so multiple // goroutines can share an encoder. .mutex.Lock()defer .mutex.Unlock()// Remove any nested writers remaining due to previous errors. .w = .w[0:1] , := validUserType(.Type())if != nil {return } .err = nil .byteBuf.Reset() .byteBuf.Write(spaceForLength) := .newEncoderState(&.byteBuf) .sendTypeDescriptor(.writer(), , ) .sendTypeId(, )if .err != nil {return .err }// Encode the object. .encode(.b, , )if .err == nil { .writeMessage(.writer(), .b) } .freeEncoderState()return .err}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.