Пакет gob
Обзор
Пакет gob управляет потоками gob — двоичными значениями, обмениваемыми между Encoder (передатчиком) и Decoder (приёмником). Типичное применение — передача аргументов и результатов удалённых вызовов процедур (RPC), таких как те, что предоставляются net/rpc.
Реализация компилирует пользовательский кодек для каждого типа данных в потоке и наиболее эффективна, когда используется единственный Encoder для передачи потока значений, амортизируя затраты на компиляцию.
Основы
Поток gob самоописателен. Каждый элемент данных в потоке предваряется спецификацией его типа, выраженной в терминах небольшого набора предопределённых типов. Указатели не передаются, но передаются вещи, на которые они указывают; то есть значения сглаживаются. Нулевые указатели запрещены, поскольку у них нет значения. Рекурсивные типы работают нормально, но рекурсивные значения (данные с циклами) проблематичны. Это может измениться.
Для использования gob создайте Encoder и представьте ему серию элементов данных как значения или адреса, которые можно разыменовать до значений. Encoder гарантирует, что вся информация о типе отправляется до того, как она потребуется. На стороне приёма Decoder извлекает значения из закодированного потока и распаковывает их в локальные переменные.
Типы и значения
Исходные и целевые значения/типы не обязательно должны точно соответствовать друг другу. Для структур поля (идентифицируемые по имени), которые есть в источнике, но отсутствуют в принимаемой переменной, будут игнорироваться. Поля, которые есть в принимаемой переменной, но отсутствуют в передаваемом типе или значении, будут игнорироваться в пункте назначения. Если поле с тем же именем присутствует в обоих, их типы должны быть совместимыми. И приёмник, и передатчик выполнят все необходимые косвенные обращения и разыменования для преобразования между gob и фактическими значениями Go. Например, тип gob, схематически,
struct { A, B int }
может быть отправлен или получен в любой из этих типов Go:
struct { A, B int } // the same
*struct { A, B int } // extra indirection of the struct
struct { *A, **B int } // extra indirection of the fields
struct { A, B int64 } // different concrete value type; see below
Он также может быть получен в любой из этих типов:
struct { A, B int } // the same
struct { B, A int } // ordering doesn't matter; matching is by name
struct { A, B, C int } // extra field (C) ignored
struct { B int } // missing field (A) ignored; data will be dropped
struct { B, C int } // missing field (A) ignored; extra field (C) ignored.
Попытка получить в эти типы вызовет ошибку декодирования:
struct { A int; B uint } // change of signedness for B
struct { A int; B float } // change of type for B
struct { } // no field names in common
struct { C, D int } // no field names in common
Целые числа передаются двумя способами: целые числа со знаком произвольной точности или целые числа без знака произвольной точности. В формате gob нет дискриминации int8, int16 и т. д.; есть только целые числа со знаком и без знака. Как описано ниже, передатчик отправляет значение в кодировании с переменной длиной; приёмник принимает значение и сохраняет его в целевой переменной. Вещественные числа всегда отправляются с использованием 64-битной точности IEEE 754 (см. ниже).
Целые числа со знаком могут быть получены в любую целую переменную со знаком: int, int16 и т. д.; целые числа без знака могут быть получены в любую целую переменную без знака; и вещественные значения могут быть получены в любую вещественную переменную. Однако целевая переменная должна уметь представлять значение, иначе операция декодирования завершится сбоем.
Также поддерживаются структуры, массивы и срезы. Структуры кодируют и декодируют только экспортированные поля. Поддерживаются строки и массивы байтов со специальным эффективным представлением (см. ниже). При декодировании среза, если у существующего среза есть ёмкость, срез будет расширен на месте; если нет, создаётся новый массив. В любом случае длина результирующего среза сообщает о количестве декодированных элементов.
В общем случае, если требуется выделение памяти, декодер выделит память. Если нет, он обновит целевые переменные значениями, считанными из потока. Он не инициализирует их сначала, поэтому, если целевое значение — составное значение, такое как карта, структура или срез, декодированные значения будут слиты по элементам в существующие переменные.
Функции и каналы не будут отправлены в gob. Попытка закодировать такое значение на верхнем уровне завершится сбоем. Поле структуры типа chan или func обрабатывается точно так же, как неэкспортированное поле, и игнорируется.
Gob может закодировать значение любого типа, реализующего интерфейсы GobEncoder или encoding.BinaryMarshaler, вызывая соответствующий метод в этом порядке предпочтения.
Gob может декодировать значение любого типа, реализующего интерфейсы GobDecoder или encoding.BinaryUnmarshaler, вызывая соответствующий метод, опять же в порядке предпочтения.
Детали кодирования
Этот раздел документирует детали кодирования, которые не важны для большинства пользователей. Детали представлены снизу вверх.
Целое число без знака отправляется одним из двух способов. Если оно меньше 128, оно отправляется как байт с этим значением. В противном случае оно отправляется как поток байтов с минимальной длиной в формате big-endian (старший байт первым), содержащий значение, предваряемый одним байтом, содержащим количество байтов, с обратным знаком. Таким образом, 0 передаётся как (00), 7 передаётся как (07), а 256 передаётся как (FE 01 00).
Булево значение кодируется в целое число без знака: 0 для ложь, 1 для истина.
Целое число со знаком, i, кодируется в целое число без знака, u. Внутри u биты с 1 и выше содержат значение; бит 0 указывает, следует ли их дополнять при приёме. Алгоритм кодирования выглядит так:
var u uint
if i < 0 {
u = (^uint(i) << 1) | 1 // complement i, bit 0 is 1
} else {
u = (uint(i) << 1) // do not complement i, bit 0 is 0
}
encodeUnsigned(u)
Следовательно, младший бит аналогичен биту знака, но его превращение в бит дополнения гарантирует, что наибольшее отрицательное целое число не является специальным случаем. Например, -129=^128=(^256>>1) кодируется как (FE 01 01).
Числа с плавающей точкой всегда передаются как представление значения float64. Это значение преобразуется в uint64 с помощью math.Float64bits. Затем uint64 инвертируется по байтам и передаётся как обычное целое без знака. Инверсия по байтам означает, что показатель степени и старшая часть мантиссы передаются первыми. Поскольку младшие биты часто равны нулю, это может сократить количество кодируемых байтов. Например, 17.0 кодируется всего тремя байтами (FE 31 40).
Строки и срезы байтов передаются как количество без знака, за которым следуют столько же не интерпретированных байтов значения.
Все остальные срезы и массивы передаются как количество без знака, за которым следуют столько же элементов с использованием стандартной кодировки gob для их типа рекурсивно.
Массивы передаются как количество без знака, за которым следуют столько же пар ключ-элемент. Пустые, но не nil массивы передаются, поэтому, если получатель ещё не выделил его, он всегда будет выделен при получении, если только передаваемый массив не равен nil и не находится на верхнем уровне.
В срезах и массивах, а также в массивах все элементы, даже элементы со значением ноль, передаются, даже если все элементы равны нулю.
Структуры передаются как последовательность пар (номер поля, значение поля). Значение поля передаётся с использованием стандартной кодировки gob для его типа рекурсивно. Если поле имеет нулевое значение для своего типа (за исключением массивов; см. выше), оно исключается из передачи. Номер поля определяется типом кодируемой структуры: первое поле кодируемого типа имеет номер 0, второе — 1 и т. д. При кодировании значения номера полей кодируются с помощью дельта-кодирования для повышения эффективности, а поля всегда передаются в порядке возрастания номера поля; дельты, следовательно, являются беззнаковыми. Инициализация дельта-кодирования устанавливает номер поля в -1, поэтому целое без знака с номером поля 0 и значением 7 передаётся как беззнаковая дельта = 1, беззнаковое значение = 7 или (01 07). Наконец, после передачи всех полей завершающая метка обозначает конец структуры. Эта метка — это значение дельты = 0, которое имеет представление (00).
Типы интерфейсов не проверяются на совместимость; все типы интерфейсов обрабатываются для передачи как члены одного типа «интерфейс», аналогично int или []byte — фактически они все обрабатываются как interface{}. Значения интерфейсов передаются как строка, определяющая конкретный тип, который передаётся (имя, которое должно быть предварительно определено вызовом Register), за которым следует количество байтов длины последующих данных (чтобы значение можно было пропустить, если оно не может быть сохранено), за которым следует обычная кодировка конкретного (динамического) значения, хранящегося в значении интерфейса. (Значение интерфейса nil идентифицируется пустой строкой и не передаёт никакого значения.) При получении декодер проверяет, что распакованный конкретный элемент удовлетворяет интерфейсу переменной получателя.
Если значение передаётся в Encoder.Encode, и тип не является структурой (или указателем на структуру и т. д.), для простоты обработки оно представляется как структура с одним полем. Единственный видимый эффект этого — кодирование нулевого байта после значения, точно так же, как после последнего поля закодированной структуры, чтобы алгоритм декодирования знал, когда значение верхнего уровня завершено.
Описание представления типов приведено ниже. Когда тип определён на данном соединении между Encoder и Decoder, ему присваивается целочисленный тип id со знаком. Когда вызывается Encoder.Encode(v), он проверяет, есть ли id, назначенный для типа v и всех его элементов, а затем отправляет пару (typeid, encoded-v), где typeid — это id типа кодированного типа v, а encoded-v — кодировка gob значения v.
Для определения типа кодировщик выбирает неиспользуемый положительный id типа и отправляет пару (-id типа, кодированный тип), где кодированный тип — это кодировка gob описания типа wireType, построенная из этих типов:
type wireType struct {
ArrayT *arrayType
SliceT *sliceType
StructT *structType
MapT *mapType
GobEncoderT *gobEncoderType
BinaryMarshalerT *gobEncoderType
TextMarshalerT *gobEncoderType
}
type arrayType struct {
CommonType
Elem typeId
Len int
}
type CommonType struct {
Name string // the name of the struct type
Id int // the id of the type, repeated so it's inside the type
}
type sliceType struct {
CommonType
Elem typeId
}
type structType struct {
CommonType
Field []fieldType // the fields of the struct.
}
type fieldType struct {
Name string // the name of the field.
Id int // the type id of the field, which must be already defined
}
type mapType struct {
CommonType
Key typeId
Elem typeId
}
type gobEncoderType struct {
CommonType
}
Если есть вложенные идентификаторы типов, типы для всех внутренних идентификаторов типов должны быть определены до использования идентификатора типа верхнего уровня для описания закодированного значения v.
Для простоты настройки соединение определяется для понимания этих типов a priori, а также основных типов gob int, uint и т. д. Их идентификаторы:
bool 1 int 2 uint 3 float 4 []byte 5 string 6 complex 7 interface 8 // gap for reserved ids. WireType 16 ArrayType 17 CommonType 18 SliceType 19 StructType 20 FieldType 21 // 22 is slice of fieldType. MapType 23
Наконец, каждое сообщение, созданное вызовом Encode, предваряется закодированным целым числом без знака, указывающим количество оставшихся байтов в сообщении. После начального имени типа значения интерфейса оборачиваются аналогичным образом; по сути, значение интерфейса действует как рекурсивный вызов Encode.
В итоге поток gob выглядит как
(byteCount (-type id, encoding of a wireType)* (type id, encoding of a value))*
где * обозначает ноль или более повторений, и идентификатор типа значения должен быть предварительно определён или определён до значения в потоке.
Совместимость: Любые будущие изменения в пакете будут направлены на сохранение совместимости с потоками, закодированными с использованием предыдущих версий. То есть любая выпущенная версия этого пакета должна иметь возможность декодировать данные, записанные любой ранее выпущенной версией, с учётом таких вопросов, как исправления ошибок безопасности. См. документ по совместимости Go для получения дополнительной информации: https://golang.org/doc/go1compat
См. раздел «Gobs of data» для обсуждения дизайна формата проволочной кодировки gob: https://blog.golang.org/gobs-of-data
Безопасность
Этот пакет не предназначен для защиты от враждебных входных данных и находится вне сферы https://go.dev/security/policy. В частности, Decoder выполняет только базовые проверки корректности размеров декодированного ввода, а его пределы не настраиваются. При декодировании данных gob из ненадежных источников необходимо проявлять осторожность, так как это может потребовать значительных ресурсов.
Пример (Базовый)
В этом примере показано базовое использование пакета: создание кодера, передача некоторых значений, их получение декодером.
Код:
package gob_test
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
type P struct {
X, Y, Z int
Name string
}
type Q struct {
X, Y *int32
Name string
}
// This example shows the basic usage of the package: Create an encoder,
// transmit some values, receive them with a decoder.
func Example_basic() {
// Initialize the encoder and decoder. Normally enc and dec would be
// bound to network connections and the encoder and decoder would
// run in different processes.
var network bytes.Buffer // Stand-in for a network connection
enc := gob.NewEncoder(&network) // Will write to network.
dec := gob.NewDecoder(&network) // Will read from network.
// Encode (send) some values.
err := enc.Encode(P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}
err = enc.Encode(P{1782, 1841, 1922, "Treehouse"})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) and print the values.
var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error 1:", err)
}
fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error 2:", err)
}
fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
// Output:
// "Pythagoras": {3, 4}
// "Treehouse": {1782, 1841}
}
Пример (EncodeDecode)
В этом примере передаётся значение, которое реализует пользовательские методы кодирования и декодирования.
Код:
package gob_test
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
// The Vector type has unexported fields, which the package cannot access.
// We therefore write a BinaryMarshal/BinaryUnmarshal method pair to allow us
// to send and receive the type with the gob package. These interfaces are
// defined in the "encoding" package.
// We could equivalently use the locally defined GobEncode/GobDecoder
// interfaces.
type Vector struct {
x, y, z int
}
func (v Vector) MarshalBinary() ([]byte, error) {
// A simple encoding: plain text.
var b bytes.Buffer
fmt.Fprintln(&b, v.x, v.y, v.z)
return b.Bytes(), nil
}
// UnmarshalBinary modifies the receiver so it must take a pointer receiver.
func (v *Vector) UnmarshalBinary(data []byte) error {
// A simple encoding: plain text.
b := bytes.NewBuffer(data)
_, err := fmt.Fscanln(b, &v.x, &v.y, &v.z)
return err
}
// This example transmits a value that implements the custom encoding and decoding methods.
func Example_encodeDecode() {
var network bytes.Buffer // Stand-in for the network.
// Create an encoder and send a value.
enc := gob.NewEncoder(&network)
err := enc.Encode(Vector{3, 4, 5})
if err != nil {
log.Fatal("encode:", err)
}
// Create a decoder and receive a value.
dec := gob.NewDecoder(&network)
var v Vector
err = dec.Decode(&v)
if err != nil {
log.Fatal("decode:", err)
}
fmt.Println(v)
// Output:
// {3 4 5}
}
Пример (Интерфейс)
В этом примере показано, как закодировать значение интерфейса. Ключевое отличие от обычных типов заключается в регистрации конкретного типа, реализующего интерфейс.
Код:
package gob_test
import (
"bytes"
"encoding/gob"
"fmt"
"log"
"math"
)
type Point struct {
X, Y int
}
func (p Point) Hypotenuse() float64 {
return math.Hypot(float64(p.X), float64(p.Y))
}
type Pythagoras interface {
Hypotenuse() float64
}
// This example shows how to encode an interface value. The key
// distinction from regular types is to register the concrete type that
// implements the interface.
func Example_interface() {
var network bytes.Buffer // Stand-in for the network.
// We must register the concrete type for the encoder and decoder (which would
// normally be on a separate machine from the encoder). On each end, this tells the
// engine which concrete type is being sent that implements the interface.
gob.Register(Point{})
// Create an encoder and send some values.
enc := gob.NewEncoder(&network)
for i := 1; i <= 3; i++ {
interfaceEncode(enc, Point{3 * i, 4 * i})
}
// Create a decoder and receive some values.
dec := gob.NewDecoder(&network)
for i := 1; i <= 3; i++ {
result := interfaceDecode(dec)
fmt.Println(result.Hypotenuse())
}
// Output:
// 5
// 10
// 15
}
// interfaceEncode encodes the interface value into the encoder.
func interfaceEncode(enc *gob.Encoder, p Pythagoras) {
// The encode will fail unless the concrete type has been
// registered. We registered it in the calling function.
// Pass pointer to interface so Encode sees (and hence sends) a value of
// interface type. If we passed p directly it would see the concrete type instead.
// See the blog post, "The Laws of Reflection" for background.
err := enc.Encode(&p)
if err != nil {
log.Fatal("encode:", err)
}
}
// interfaceDecode decodes the next interface value from the stream and returns it.
func interfaceDecode(dec *gob.Decoder) Pythagoras {
// The decode will fail unless the concrete type on the wire has been
// registered. We registered it in the calling function.
var p Pythagoras
err := dec.Decode(&p)
if err != nil {
log.Fatal("decode:", err)
}
return p
}
Индекс
Файлы пакета
dec_helpers.go decode.go decoder.go doc.go enc_helpers.go encode.go encoder.go error.go type.go
func Register
func Register(value any)
Register записывает тип, идентифицированный значением для этого типа, под его внутренним именем типа. Это имя будет идентифицировать конкретный тип значения, отправленного или полученного как переменная интерфейса. Регистрировать необходимо только типы, которые будут передаваться как реализации значений интерфейса. Ожидается, что будет использоваться только во время инициализации; в противном случае, вызывает панику, если отображение между типами и именами не является взаимно однозначным.
func RegisterName
func RegisterName(name string, value any)
RegisterName аналогичен Register, но использует предоставленное имя вместо значения по умолчанию типа.
type CommonType
CommonType хранит элементы всех типов. Это исторический артефакт, сохранённый для обеспечения двоичной совместимости и экспортированный только для удобства кодирования пакета описаний типов. Он не предназначен для прямого использования клиентами.
type CommonType struct {
Name string
Id typeId
}
type Decoder
Decoder управляет приёмом информации о типе и данных, считываемых с удалённой стороны соединения. Он безопасен для одновременного использования несколькими горутинами.
Decoder выполняет только базовые проверки целостности размеров декодированного ввода, и его пределы не настраиваются. Будьте осторожны при декодировании данных gob из ненадежных источников.
type Decoder struct {
// contains filtered or unexported fields
}
func NewDecoder
func NewDecoder(r io.Reader) *Decoder
NewDecoder возвращает новый декодер, который считывает из io.Reader. Если r также не реализует io.ByteReader, он будет обернут в bufio.Reader.
func (*Decoder) Decode
func (dec *Decoder) Decode(e any) error
Decode считывает следующее значение из потока ввода и сохраняет его в данные, представленные значением пустого интерфейса. Если e равно null, значение будет проигнорировано. В противном случае, значение, лежащее в основе e, должно быть указателем на правильный тип для следующего полученного элемента данных. Если входной поток достиг EOF, Decode возвращает io.EOF и не изменяет e.
func (*Decoder) DecodeValue
func (dec *Decoder) DecodeValue(v reflect.Value) error
DecodeValue считывает следующее значение из потока ввода. Если v является нулевым значением reflect.Value (v.Kind() == Invalid), DecodeValue игнорирует это значение. В противном случае, оно сохраняет значение в v. В этом случае v должен представлять непустой указатель на данные или быть присваиваемым значением reflect.Value (v.CanSet()). Если входной поток достиг EOF, DecodeValue возвращает io.EOF и не изменяет v.
type Encoder
Encoder управляет передачей информации о типе и данных на другую сторону соединения. Он безопасен для одновременного использования несколькими горутинами.
type Encoder struct {
// contains filtered or unexported fields
}
func NewEncoder
func NewEncoder(w io.Writer) *Encoder
NewEncoder возвращает новый энкодер, который будет передавать данные в io.Writer.
func (*Encoder) Encode
func (enc *Encoder) Encode(e any) error
Encode передаёт элемент данных, представленный значением пустого интерфейса, гарантируя, что сначала была передана вся необходимая информация о типе. Передача нулевого указателя в Encoder вызовет панику, так как они не могут быть переданы через gob.
func (*Encoder) EncodeValue
func (enc *Encoder) EncodeValue(value reflect.Value) error
EncodeValue передаёт элемент данных, представленный значением отражения, гарантируя, что сначала была передана вся необходимая информация о типе. Передача нулевого указателя в EncodeValue вызовет панику, так как они не могут быть переданы через gob.
type GobDecoder
GobDecoder — это интерфейс, описывающий данные, которые предоставляют свою собственную процедуру декодирования переданных значений, отправленных GobEncoder.
type GobDecoder interface {
// GobDecode overwrites the receiver, which must be a pointer,
// with the value represented by the byte slice, which was written
// by GobEncode, usually for the same concrete type.
GobDecode([]byte) error
} тип GobEncoder
GobEncoder — это интерфейс, описывающий данные, которые предоставляют собственное представление для кодирования значений для передачи GobDecoder. Тип, реализующий GobEncoder и GobDecoder, имеет полный контроль над представлением своих данных и может, следовательно, содержать такие вещи, как закрытые поля, каналы и функции, которые обычно не передаются в потоках gob.
Примечание: поскольку gobs могут храниться постоянно, хорошим дизайном является гарантия стабильности кодирования, используемого GobEncoder, по мере развития программного обеспечения. Например, может иметь смысл, чтобы GobEncode включал номер версии в кодирование.
type GobEncoder interface {
// GobEncode returns a byte slice representing the encoding of the
// receiver for transmission to a GobDecoder, usually of the same
// concrete type.
GobEncode() ([]byte, error)
}
© Google, Inc.
Licensed under the Creative Commons Attribution License 3.0.
http://golang.org/pkg/encoding/gob/