Пакет 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 и т. д.; существуют только целые числа со знаком и без знака. Как описано ниже, отправитель передаёт значение в кодировке переменной длины; получатель принимает значение и сохраняет его в целевой переменной. Числа с плавающей точкой всегда передаются с точностью IEEE 754 в 64 бита (см. ниже).
Целые числа со знаком можно получить в любую целочисленную переменную со знаком: int, int16 и т. д.; целые числа без знака — в любую целочисленную переменную без знака; значения с плавающей точкой — в любую переменную с плавающей точкой. Однако целевая переменная должна быть способна представить значение, иначе операция декодирования завершится ошибкой.
Также поддерживаются структуры, массивы и срезы. Структуры кодируют и декодируют только экспортируемые поля. Строки и массивы байтов поддерживаются с помощью специального эффективного представления (см. ниже). При декодировании среза, если у существующего среза есть ёмкость, он будет расширен на месте; в противном случае будет выделен новый массив. В любом случае длина итогового среза указывает количество декодированных элементов.
Как правило, если требуется выделение памяти, декодер выделяет её. В противном случае он обновляет целевые переменные значениями, прочитанными из потока. Предварительная инициализация не выполняется, поэтому, если целевое значение является составным, например картой, структурой или срезом, декодированные значения будут поэлементно объединены с существующими переменными.
Функции и каналы не передаются в gob. Попытка закодировать такое значение на верхнем уровне завершится ошибкой. Поле структуры типа chan или func обрабатывается точно так же, как неэкспортируемое поле, и игнорируется.
Gob может кодировать значение любого типа, реализующего интерфейс GobEncoder или encoding.BinaryMarshaler, вызывая соответствующий метод в указанном порядке предпочтения.
Gob может декодировать значение любого типа, реализующего интерфейс GobDecoder или encoding.BinaryUnmarshaler, вызывая соответствующий метод, опять же в указанном порядке предпочтения.
Подробности кодирования
В этом разделе описываются подробности кодирования, несущественные для большинства пользователей. Описание дано от нижнего уровня к верхнему.
Целое число без знака передаётся одним из двух способов. Если оно меньше 128, оно передаётся в виде байта с этим значением. В противном случае оно передаётся в виде минимальной по длине последовательности байтов в порядке от старшего к младшему, содержащей это значение и предваряемой одним байтом, в котором указано отрицательное количество байтов. Таким образом, 0 передаётся как (00), 7 — как (07), а 256 — как (FE 01 00).
Логическое значение кодируется как целое число без знака: 0 для false, 1 для true.
Целое число со знаком 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 для соответствующего типа.
Карты передаются как целое число без знака, обозначающее количество пар «ключ — элемент», за которым следуют эти пары. Пустые, но не нулевые карты передаются; поэтому, если получатель ещё не выделил память для карты, при получении она будет выделена, если только переданная карта не является нулевой и не находится на верхнем уровне.
В срезах, массивах и картах передаются все элементы, включая элементы с нулевыми значениями, даже если все элементы нулевые.
Структуры передаются как последовательность пар (номер поля, значение поля). Значение поля рекурсивно передаётся с использованием стандартного кодирования gob для его типа. Если поле имеет нулевое значение своего типа (за исключением массивов; см. выше) или является указателем на нулевое значение, оно не передаётся. Номер поля определяется типом кодируемой структуры: первое поле кодируемого типа имеет номер 0, второе — номер 1 и т. д. При кодировании значения номера полей дельта-кодируются для повышения эффективности, а поля всегда передаются в порядке возрастания номеров; поэтому дельты являются числами без знака. Для инициализации дельта-кодирования номер поля устанавливается в -1, поэтому целочисленное поле без знака 0 со значением 7 передаётся как беззнаковая дельта = 1, беззнаковое значение = 7, то есть (01 07). Наконец, после передачи всех полей завершающая метка обозначает конец структуры. Эта метка представляет собой значение delta=0, кодируемое как (00).
Типы интерфейсов не проверяются на совместимость; при передаче все типы интерфейсов рассматриваются как элементы единого типа «interface», аналогичного int или []byte, — фактически все они считаются interface{}. Значения интерфейсов передаются как строка, идентифицирующая конкретный тип (имя, которое должно быть предварительно зарегистрировано вызовом Register), за которой следует количество байтов следующих данных (чтобы значение можно было пропустить, если его невозможно сохранить), а затем обычное кодирование конкретного (динамического) значения, хранящегося в значении интерфейса. (Нулевое значение интерфейса обозначается пустой строкой и не передаёт значение.) При получении декодер проверяет, соответствует ли распакованный конкретный элемент интерфейсу принимающей переменной.
Если в Encoder.Encode передано значение, тип которого не является структурой (или указателем на структуру и т. д.), для упрощения обработки оно представляется как структура с одним полем. Единственное заметное следствие этого — после значения кодируется нулевой байт, как и после последнего поля закодированной структуры, чтобы алгоритм декодирования мог определить, когда значение верхнего уровня завершено.
Представление типов описано ниже. При определении типа для данного соединения между Encoder и Decoder ему присваивается целочисленный идентификатор типа со знаком. При вызове Encoder.Encode(v) метод проверяет, присвоен ли идентификатор типу v и всем его элементам, а затем отправляет пару (typeid, encoded-v), где typeid — идентификатор закодированного типа v, а encoded-v — gob-кодирование значения v.
Для определения типа кодировщик выбирает неиспользуемый положительный идентификатор типа и отправляет пару (-type id, encoded-type), где encoded-type — 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
}
Если имеются вложенные идентификаторы типов, типы для всех внутренних идентификаторов должны быть определены до того, как идентификатор типа верхнего уровня будет использован для описания encoded-v.
Для упрощения настройки считается, что соединение заранее распознаёт эти типы, а также базовые типы 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
Обсуждение проекта формата передачи gob см. в статье «Gobs of data»: https://go.dev/blog/gob
Безопасность
Этот пакет не предназначен для защиты от вредоносных входных данных и не входит в область действия 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}
}
Пример (Interface)
В этом примере показано, как закодировать значение интерфейса. Главное отличие от обычных типов — необходимость зарегистрировать конкретный тип, реализующий интерфейс.
Код:
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 равно nil, значение будет отброшено. В противном случае значение, лежащее в основе e, должно быть указателем на тип, соответствующий следующему полученному элементу данных. Если достигнут конец входных данных, 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()). Если достигнут конец входных данных, 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 передаёт элемент данных, представленный значением reflection, гарантируя, что вся необходимая информация о типах будет передана заранее. Передача 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
} type GobEncoder
GobEncoder — это интерфейс, описывающий данные, которые предоставляют собственное представление для кодирования значений с целью передачи GobDecoder. Тип, реализующий GobEncoder и GobDecoder, полностью контролирует представление своих данных и поэтому может содержать такие элементы, как приватные поля, каналы и функции, которые обычно нельзя передавать в потоках gob.
Примечание: поскольку данные gob можно хранить постоянно, рекомендуется гарантировать стабильность кодирования, используемого 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/