This commit is contained in:
Joshua Bell 2026-01-26 21:41:26 -06:00
parent 07ec5529ac
commit f501abe660
532 changed files with 271781 additions and 0 deletions

View file

@ -0,0 +1,47 @@
// Copyright 2018 The go-libvirt 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.
//
// Code generated by internal/lvgen/generate.go. DO NOT EDIT.
//
// To regenerate, run 'go generate' in internal/lvgen.
//
package constants
// These are libvirt procedure numbers which correspond to each respective
// API call between remote_internal driver and libvirtd. Each procedure is
// identified by a unique number.
const (
// From enums:
// QEMUProcDomainMonitorCommand is libvirt's QEMU_PROC_DOMAIN_MONITOR_COMMAND
QEMUProcDomainMonitorCommand = 1
// QEMUProcDomainAttach is libvirt's QEMU_PROC_DOMAIN_ATTACH
QEMUProcDomainAttach = 2
// QEMUProcDomainAgentCommand is libvirt's QEMU_PROC_DOMAIN_AGENT_COMMAND
QEMUProcDomainAgentCommand = 3
// QEMUProcConnectDomainMonitorEventRegister is libvirt's QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER
QEMUProcConnectDomainMonitorEventRegister = 4
// QEMUProcConnectDomainMonitorEventDeregister is libvirt's QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER
QEMUProcConnectDomainMonitorEventDeregister = 5
// QEMUProcDomainMonitorEvent is libvirt's QEMU_PROC_DOMAIN_MONITOR_EVENT
QEMUProcDomainMonitorEvent = 6
// From consts:
// QEMUProgram is libvirt's QEMU_PROGRAM
QEMUProgram = 0x20008087
// QEMUProtocolVersion is libvirt's QEMU_PROTOCOL_VERSION
QEMUProtocolVersion = 1
)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
// Copyright 2020 The go-libvirt 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 event
// Event represents an internal Event.
type Event interface {
GetCallbackID() int32
}

View file

@ -0,0 +1,157 @@
// Copyright 2020 The go-libvirt 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 event
import (
"context"
)
// emptyEvent is used as a zero-value. Clients will never receive one of these;
// they are only here to satisfy the compiler. See the comments in process() for
// more information.
type emptyEvent struct{}
func (emptyEvent) GetCallbackID() int32 { return 0 }
// Stream is an unbounded buffered event channel. The implementation
// consists of a pair of unbuffered channels and a goroutine to manage them.
// Client behavior will not cause incoming events to block.
type Stream struct {
// Program specifies the source of the events - libvirt or QEMU.
Program uint32
// CallbackID is returned by the event registration call.
CallbackID int32
// manage unbounded channel behavior.
queue []Event
qlen chan (chan int)
in, out chan Event
// terminates processing
shutdown context.CancelFunc
}
// NewStream configures a new Event Stream. Incoming events are appended to a
// queue, which is then relayed to the listening client. Client behavior will
// not cause incoming events to block. It is the responsibility of the caller
// to terminate the Stream via Shutdown() when no longer in use.
func NewStream(program uint32, cbID int32) *Stream {
s := &Stream{
Program: program,
CallbackID: cbID,
in: make(chan Event),
out: make(chan Event),
qlen: make(chan (chan int)),
}
// Start the processing loop, which will return a routine we can use to
// shut the queue down later.
s.shutdown = s.start()
return s
}
// Len will return the current count of events in the internal queue for a
// stream. It does this by sending a message to the stream's process() loop,
// which will then write the current length to the channel contained in that
// message.
func (s *Stream) Len() int {
// Send a request to the process() loop to get the current length of the
// queue
ch := make(chan int)
s.qlen <- ch
return <-ch
}
// Recv returns the next available event from the Stream's queue.
func (s *Stream) Recv() chan Event {
return s.out
}
// Push appends a new event to the queue.
func (s *Stream) Push(e Event) {
s.in <- e
}
// Shutdown gracefully terminates Stream processing, releasing all internal
// resources. Events which have not yet been received by the client will be
// dropped. Subsequent calls to Shutdown() are idempotent.
func (s *Stream) Shutdown() {
if s.shutdown != nil {
s.shutdown()
}
}
// start starts the event processing loop, which will continue to run until
// terminated by the returned context.CancelFunc.
func (s *Stream) start() context.CancelFunc {
ctx, cancel := context.WithCancel(context.Background())
go s.process(ctx)
return cancel
}
// process manages an Stream's lifecycle until canceled by the provided context.
// Incoming events are appended to a queue which is then relayed to the
// listening client. New events pushed onto the queue will not block if the
// client is not actively polling for them; the stream will buffer them
// internally.
func (s *Stream) process(ctx context.Context) {
// Close the output channel so that clients know this stream is finished.
// We don't close s.in to avoid creating a race with the stream's Push()
// function.
defer close(s.out)
// This function is used to retrieve the next event from the queue, to be
// sent to the client. If there are no more events to send, it returns a nil
// channel and a zero-value event.
nextEvent := func() (chan Event, Event) {
sendCh := chan Event(nil)
next := Event(emptyEvent{})
if len(s.queue) > 0 {
sendCh = s.out
next = s.queue[0]
}
return sendCh, next
}
// The select statement in this loop relies on the fact that a send to a nil
// channel will block forever. If we have no entries in the queue, the
// sendCh variable will be nil, so the clause that attempts to send an event
// to the client will never complete. Clients will never receive an
// emptyEvent.
for {
sendCh, nextEvt := nextEvent()
select {
// new event received, append to queue
case e := <-s.in:
s.queue = append(s.queue, e)
case lenCh := <-s.qlen:
lenCh <- len(s.queue)
// client received an event, pop from queue
case sendCh <- nextEvt:
s.queue = s.queue[1:]
// shutdown requested
case <-ctx.Done():
return
}
}
}

View file

@ -0,0 +1,13 @@
Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View file

@ -0,0 +1,896 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package xdr
import (
"fmt"
"io"
"math"
"reflect"
"time"
)
var (
errMaxSlice = "data exceeds max slice limit"
errIODecode = "%s while decoding %d bytes"
)
/*
Unmarshal parses XDR-encoded data into the value pointed to by v reading from
reader r and returning the total number of bytes read. An addressable pointer
must be provided since Unmarshal needs to both store the result of the decode as
well as obtain target type information. Unmarhsal traverses v recursively and
automatically indirects pointers through arbitrary depth, allocating them as
necessary, to decode the data into the underlying value pointed to.
Unmarshal uses reflection to determine the type of the concrete value contained
by v and performs a mapping of underlying XDR types to Go types as follows:
Go Type <- XDR Type
--------------------
int8, int16, int32, int <- XDR Integer
uint8, uint16, uint32, uint <- XDR Unsigned Integer
int64 <- XDR Hyper Integer
uint64 <- XDR Unsigned Hyper Integer
bool <- XDR Boolean
float32 <- XDR Floating-Point
float64 <- XDR Double-Precision Floating-Point
string <- XDR String
byte <- XDR Integer
[]byte <- XDR Variable-Length Opaque Data
[#]byte <- XDR Fixed-Length Opaque Data
[]<type> <- XDR Variable-Length Array
[#]<type> <- XDR Fixed-Length Array
struct <- XDR Structure
map <- XDR Variable-Length Array of two-element XDR Structures
time.Time <- XDR String encoded with RFC3339 nanosecond precision
Notes and Limitations:
* Automatic unmarshalling of variable and fixed-length arrays of uint8s
requires a special struct tag `xdropaque:"false"` since byte slices
and byte arrays are assumed to be opaque data and byte is a Go alias
for uint8 thus indistinguishable under reflection
* Cyclic data structures are not supported and will result in infinite
loops
If any issues are encountered during the unmarshalling process, an
UnmarshalError is returned with a human readable description as well as
an ErrorCode value for further inspection from sophisticated callers. Some
potential issues are unsupported Go types, attempting to decode a value which is
too large to fit into a specified Go type, and exceeding max slice limitations.
*/
func Unmarshal(r io.Reader, v interface{}) (int, error) {
d := Decoder{r: r}
return d.Decode(v)
}
// UnmarshalLimited is identical to Unmarshal but it sets maxReadSize in order
// to cap reads.
func UnmarshalLimited(r io.Reader, v interface{}, maxSize uint) (int, error) {
d := Decoder{r: r, maxReadSize: maxSize}
return d.Decode(v)
}
// TypeDecoder lets a caller provide a custom decode routine for a custom type.
type TypeDecoder interface {
Decode(*Decoder, reflect.Value) (int, error)
}
// A Decoder wraps an io.Reader that is expected to provide an XDR-encoded byte
// stream and provides several exposed methods to manually decode various XDR
// primitives without relying on reflection. The NewDecoder function can be
// used to get a new Decoder directly.
//
// Typically, Unmarshal should be used instead of manual decoding. A Decoder
// is exposed so it is possible to perform manual decoding should it be
// necessary in complex scenarios where automatic reflection-based decoding
// won't work.
type Decoder struct {
r io.Reader
// maxReadSize is the default maximum bytes an element can contain. 0
// is unlimited and provides backwards compatability. Setting it to a
// non-zero value caps reads.
maxReadSize uint
// customTypes is a map allowing the caller to provide decoder routines for
// custom types known only to itself.
customTypes map[string]TypeDecoder
}
// DecodeInt treats the next 4 bytes as an XDR encoded integer and returns the
// result as an int32 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.1 - Integer
// 32-bit big-endian signed integer in range [-2147483648, 2147483647]
func (d *Decoder) DecodeInt() (int32, int, error) {
var buf [4]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 4)
err := unmarshalError("DecodeInt", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := int32(buf[3]) | int32(buf[2])<<8 |
int32(buf[1])<<16 | int32(buf[0])<<24
return rv, n, nil
}
// DecodeUint treats the next 4 bytes as an XDR encoded unsigned integer and
// returns the result as a uint32 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.2 - Unsigned Integer
// 32-bit big-endian unsigned integer in range [0, 4294967295]
func (d *Decoder) DecodeUint() (uint32, int, error) {
var buf [4]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 4)
err := unmarshalError("DecodeUint", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := uint32(buf[3]) | uint32(buf[2])<<8 |
uint32(buf[1])<<16 | uint32(buf[0])<<24
return rv, n, nil
}
// DecodeEnum treats the next 4 bytes as an XDR encoded enumeration value and
// returns the result as an int32 after verifying that the value is in the
// provided map of valid values. It also returns the number of bytes actually
// read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the parsed enumeration value is not one of the provided valid values.
//
// Reference:
// RFC Section 4.3 - Enumeration
// Represented as an XDR encoded signed integer
func (d *Decoder) DecodeEnum(validEnums map[int32]bool) (int32, int, error) {
val, n, err := d.DecodeInt()
if err != nil {
return 0, n, err
}
if !validEnums[val] {
err := unmarshalError("DecodeEnum", ErrBadEnumValue,
"invalid enum", val, nil)
return 0, n, err
}
return val, n, nil
}
// DecodeBool treats the next 4 bytes as an XDR encoded boolean value and
// returns the result as a bool along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the parsed value is not a 0 or 1.
//
// Reference:
// RFC Section 4.4 - Boolean
// Represented as an XDR encoded enumeration where 0 is false and 1 is true
func (d *Decoder) DecodeBool() (bool, int, error) {
val, n, err := d.DecodeInt()
if err != nil {
return false, n, err
}
switch val {
case 0:
return false, n, nil
case 1:
return true, n, nil
}
err = unmarshalError("DecodeBool", ErrBadEnumValue, "bool not 0 or 1",
val, nil)
return false, n, err
}
// DecodeHyper treats the next 8 bytes as an XDR encoded hyper value and
// returns the result as an int64 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.5 - Hyper Integer
// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807]
func (d *Decoder) DecodeHyper() (int64, int, error) {
var buf [8]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 8)
err := unmarshalError("DecodeHyper", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := int64(buf[7]) | int64(buf[6])<<8 |
int64(buf[5])<<16 | int64(buf[4])<<24 |
int64(buf[3])<<32 | int64(buf[2])<<40 |
int64(buf[1])<<48 | int64(buf[0])<<56
return rv, n, err
}
// DecodeUhyper treats the next 8 bytes as an XDR encoded unsigned hyper value
// and returns the result as a uint64 along with the number of bytes actually
// read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.5 - Unsigned Hyper Integer
// 64-bit big-endian unsigned integer in range [0, 18446744073709551615]
func (d *Decoder) DecodeUhyper() (uint64, int, error) {
var buf [8]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 8)
err := unmarshalError("DecodeUhyper", ErrIO, msg, buf[:n], err)
return 0, n, err
}
rv := uint64(buf[7]) | uint64(buf[6])<<8 |
uint64(buf[5])<<16 | uint64(buf[4])<<24 |
uint64(buf[3])<<32 | uint64(buf[2])<<40 |
uint64(buf[1])<<48 | uint64(buf[0])<<56
return rv, n, nil
}
// DecodeFloat treats the next 4 bytes as an XDR encoded floating point and
// returns the result as a float32 along with the number of bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.6 - Floating Point
// 32-bit single-precision IEEE 754 floating point
func (d *Decoder) DecodeFloat() (float32, int, error) {
var buf [4]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 4)
err := unmarshalError("DecodeFloat", ErrIO, msg, buf[:n], err)
return 0, n, err
}
val := uint32(buf[3]) | uint32(buf[2])<<8 |
uint32(buf[1])<<16 | uint32(buf[0])<<24
return math.Float32frombits(val), n, nil
}
// DecodeDouble treats the next 8 bytes as an XDR encoded double-precision
// floating point and returns the result as a float64 along with the number of
// bytes actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining.
//
// Reference:
// RFC Section 4.7 - Double-Precision Floating Point
// 64-bit double-precision IEEE 754 floating point
func (d *Decoder) DecodeDouble() (float64, int, error) {
var buf [8]byte
n, err := io.ReadFull(d.r, buf[:])
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), 8)
err := unmarshalError("DecodeDouble", ErrIO, msg, buf[:n], err)
return 0, n, err
}
val := uint64(buf[7]) | uint64(buf[6])<<8 |
uint64(buf[5])<<16 | uint64(buf[4])<<24 |
uint64(buf[3])<<32 | uint64(buf[2])<<40 |
uint64(buf[1])<<48 | uint64(buf[0])<<56
return math.Float64frombits(val), n, nil
}
// RFC Section 4.8 - Quadruple-Precision Floating Point
// 128-bit quadruple-precision floating point
// Not Implemented
// DecodeFixedOpaque treats the next 'size' bytes as XDR encoded opaque data and
// returns the result as a byte slice along with the number of bytes actually
// read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining to
// satisfy the passed size, including the necessary padding to make it a
// multiple of 4.
//
// Reference:
// RFC Section 4.9 - Fixed-Length Opaque Data
// Fixed-length uninterpreted data zero-padded to a multiple of four
func (d *Decoder) DecodeFixedOpaque(size int32) ([]byte, int, error) {
// Nothing to do if size is 0.
if size == 0 {
return nil, 0, nil
}
pad := (4 - (size % 4)) % 4
paddedSize := size + pad
if uint(paddedSize) > uint(math.MaxInt32) {
err := unmarshalError("DecodeFixedOpaque", ErrOverflow,
errMaxSlice, paddedSize, nil)
return nil, 0, err
}
buf := make([]byte, paddedSize)
n, err := io.ReadFull(d.r, buf)
if err != nil {
msg := fmt.Sprintf(errIODecode, err.Error(), paddedSize)
err := unmarshalError("DecodeFixedOpaque", ErrIO, msg, buf[:n],
err)
return nil, n, err
}
return buf[0:size], n, nil
}
// DecodeOpaque treats the next bytes as variable length XDR encoded opaque
// data and returns the result as a byte slice along with the number of bytes
// actually read.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the opaque data is larger than the max length of a Go slice.
//
// Reference:
// RFC Section 4.10 - Variable-Length Opaque Data
// Unsigned integer length followed by fixed opaque data of that length
func (d *Decoder) DecodeOpaque() ([]byte, int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return nil, n, err
}
if uint(dataLen) > uint(math.MaxInt32) ||
(d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) {
err := unmarshalError("DecodeOpaque", ErrOverflow, errMaxSlice,
dataLen, nil)
return nil, n, err
}
rv, n2, err := d.DecodeFixedOpaque(int32(dataLen))
n += n2
if err != nil {
return nil, n, err
}
return rv, n, nil
}
// DecodeString treats the next bytes as a variable length XDR encoded string
// and returns the result as a string along with the number of bytes actually
// read. Character encoding is assumed to be UTF-8 and therefore ASCII
// compatible. If the underlying character encoding is not compatibile with
// this assumption, the data can instead be read as variable-length opaque data
// (DecodeOpaque) and manually converted as needed.
//
// An UnmarshalError is returned if there are insufficient bytes remaining or
// the string data is larger than the max length of a Go slice.
//
// Reference:
// RFC Section 4.11 - String
// Unsigned integer length followed by bytes zero-padded to a multiple of
// four
func (d *Decoder) DecodeString() (string, int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return "", n, err
}
if uint(dataLen) > uint(math.MaxInt32) ||
(d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) {
err = unmarshalError("DecodeString", ErrOverflow, errMaxSlice,
dataLen, nil)
return "", n, err
}
opaque, n2, err := d.DecodeFixedOpaque(int32(dataLen))
n += n2
if err != nil {
return "", n, err
}
return string(opaque), n, nil
}
// decodeFixedArray treats the next bytes as a series of XDR encoded elements
// of the same type as the array represented by the reflection value and decodes
// each element into the passed array. The ignoreOpaque flag controls whether
// or not uint8 (byte) elements should be decoded individually or as a fixed
// sequence of opaque data. It returns the the number of bytes actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the array elements.
//
// Reference:
// RFC Section 4.12 - Fixed-Length Array
// Individually XDR encoded array elements
func (d *Decoder) decodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) {
// Treat [#]byte (byte is alias for uint8) as opaque data unless
// ignored.
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
data, n, err := d.DecodeFixedOpaque(int32(v.Len()))
if err != nil {
return n, err
}
reflect.Copy(v, reflect.ValueOf(data))
return n, nil
}
// Decode each array element.
var n int
for i := 0; i < v.Len(); i++ {
n2, err := d.decode(v.Index(i))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// decodeArray treats the next bytes as a variable length series of XDR encoded
// elements of the same type as the array represented by the reflection value.
// The number of elements is obtained by first decoding the unsigned integer
// element count. Then each element is decoded into the passed array. The
// ignoreOpaque flag controls whether or not uint8 (byte) elements should be
// decoded individually or as a variable sequence of opaque data. It returns
// the number of bytes actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the array elements.
//
// Reference:
// RFC Section 4.13 - Variable-Length Array
// Unsigned integer length followed by individually XDR encoded array
// elements
func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return n, err
}
if uint(dataLen) > uint(math.MaxInt32) ||
(d.maxReadSize != 0 && uint(dataLen) > d.maxReadSize) {
err := unmarshalError("decodeArray", ErrOverflow, errMaxSlice,
dataLen, nil)
return n, err
}
// Allocate storage for the slice elements (the underlying array) if
// existing slice does not have enough capacity.
sliceLen := int(dataLen)
if v.Cap() < sliceLen {
v.Set(reflect.MakeSlice(v.Type(), sliceLen, sliceLen))
}
if v.Len() < sliceLen {
v.SetLen(sliceLen)
}
// Treat []byte (byte is alias for uint8) as opaque data unless ignored.
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
data, n2, err := d.DecodeFixedOpaque(int32(sliceLen))
n += n2
if err != nil {
return n, err
}
v.SetBytes(data)
return n, nil
}
// Decode each slice element.
for i := 0; i < sliceLen; i++ {
n2, err := d.decode(v.Index(i))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// decodeStruct treats the next bytes as a series of XDR encoded elements
// of the same type as the exported fields of the struct represented by the
// passed reflection value. Pointers are automatically indirected and
// allocated as necessary. It returns the the number of bytes actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the elements.
//
// Reference:
// RFC Section 4.14 - Structure
// XDR encoded elements in the order of their declaration in the struct
func (d *Decoder) decodeStruct(v reflect.Value) (int, error) {
var n int
vt := v.Type()
for i := 0; i < v.NumField(); i++ {
// Skip unexported fields.
vtf := vt.Field(i)
if vtf.PkgPath != "" {
continue
}
// Indirect through pointers allocating them as needed and
// ensure the field is settable.
vf := v.Field(i)
vf, err := d.indirect(vf)
if err != nil {
return n, err
}
if !vf.CanSet() {
msg := fmt.Sprintf("can't decode to unsettable '%v'",
vf.Type().String())
err := unmarshalError("decodeStruct", ErrNotSettable,
msg, nil, nil)
return n, err
}
// Handle non-opaque data to []uint8 and [#]uint8 based on
// struct tag.
tag := vtf.Tag.Get("xdropaque")
if tag == "false" {
switch vf.Kind() {
case reflect.Slice:
n2, err := d.decodeArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
case reflect.Array:
n2, err := d.decodeFixedArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
}
}
// Decode each struct field.
n2, err := d.decode(vf)
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// RFC Section 4.15 - Discriminated Union
// RFC Section 4.16 - Void
// RFC Section 4.17 - Constant
// RFC Section 4.18 - Typedef
// RFC Section 4.19 - Optional data
// RFC Sections 4.15 though 4.19 only apply to the data specification language
// which is not implemented by this package. In the case of discriminated
// unions, struct tags are used to perform a similar function.
// decodeMap treats the next bytes as an XDR encoded variable array of 2-element
// structures whose fields are of the same type as the map keys and elements
// represented by the passed reflection value. Pointers are automatically
// indirected and allocated as necessary. It returns the the number of bytes
// actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the elements.
func (d *Decoder) decodeMap(v reflect.Value) (int, error) {
dataLen, n, err := d.DecodeUint()
if err != nil {
return n, err
}
// Allocate storage for the underlying map if needed.
vt := v.Type()
if v.IsNil() {
v.Set(reflect.MakeMap(vt))
}
// Decode each key and value according to their type.
keyType := vt.Key()
elemType := vt.Elem()
for i := uint32(0); i < dataLen; i++ {
key := reflect.New(keyType).Elem()
n2, err := d.decode(key)
n += n2
if err != nil {
return n, err
}
val := reflect.New(elemType).Elem()
n2, err = d.decode(val)
n += n2
if err != nil {
return n, err
}
v.SetMapIndex(key, val)
}
return n, nil
}
// decodeInterface examines the interface represented by the passed reflection
// value to detect whether it is an interface that can be decoded into and
// if it is, extracts the underlying value to pass back into the decode function
// for decoding according to its type. It returns the the number of bytes
// actually read.
//
// An UnmarshalError is returned if any issues are encountered while decoding
// the interface.
func (d *Decoder) decodeInterface(v reflect.Value) (int, error) {
if v.IsNil() || !v.CanInterface() {
msg := fmt.Sprintf("can't decode to nil interface")
err := unmarshalError("decodeInterface", ErrNilInterface, msg,
nil, nil)
return 0, err
}
// Extract underlying value from the interface and indirect through
// pointers allocating them as needed.
ve := reflect.ValueOf(v.Interface())
ve, err := d.indirect(ve)
if err != nil {
return 0, err
}
if !ve.CanSet() {
msg := fmt.Sprintf("can't decode to unsettable '%v'",
ve.Type().String())
err := unmarshalError("decodeInterface", ErrNotSettable, msg,
nil, nil)
return 0, err
}
return d.decode(ve)
}
// decode is the main workhorse for unmarshalling via reflection. It uses
// the passed reflection value to choose the XDR primitives to decode from
// the encapsulated reader. It is a recursive function,
// so cyclic data structures are not supported and will result in an infinite
// loop. It returns the the number of bytes actually read.
func (d *Decoder) decode(v reflect.Value) (int, error) {
if !v.IsValid() {
msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String())
err := unmarshalError("decode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// Indirect through pointers allocating them as needed.
ve, err := d.indirect(v)
if err != nil {
return 0, err
}
// Handle time.Time values by decoding them as an RFC3339 formatted
// string with nanosecond precision. Check the type string rather
// than doing a full blown conversion to interface and type assertion
// since checking a string is much quicker.
switch ve.Type().String() {
case "time.Time":
// Read the value as a string and parse it.
timeString, n, err := d.DecodeString()
if err != nil {
return n, err
}
ttv, err := time.Parse(time.RFC3339, timeString)
if err != nil {
err := unmarshalError("decode", ErrParseTime,
err.Error(), timeString, err)
return n, err
}
ve.Set(reflect.ValueOf(ttv))
return n, nil
}
// If this type is in our custom types map, call the decode routine set up
// for it.
if dt, ok := d.customTypes[ve.Type().String()]; ok {
return dt.Decode(d, v)
}
// Handle native Go types.
switch ve.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int:
i, n, err := d.DecodeInt()
if err != nil {
return n, err
}
if ve.OverflowInt(int64(i)) {
msg := fmt.Sprintf("signed integer too large to fit '%s'",
ve.Kind().String())
err = unmarshalError("decode", ErrOverflow, msg, i, nil)
return n, err
}
ve.SetInt(int64(i))
return n, nil
case reflect.Int64:
i, n, err := d.DecodeHyper()
if err != nil {
return n, err
}
ve.SetInt(i)
return n, nil
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint:
ui, n, err := d.DecodeUint()
if err != nil {
return n, err
}
if ve.OverflowUint(uint64(ui)) {
msg := fmt.Sprintf("unsigned integer too large to fit '%s'",
ve.Kind().String())
err = unmarshalError("decode", ErrOverflow, msg, ui, nil)
return n, err
}
ve.SetUint(uint64(ui))
return n, nil
case reflect.Uint64:
ui, n, err := d.DecodeUhyper()
if err != nil {
return n, err
}
ve.SetUint(ui)
return n, nil
case reflect.Bool:
b, n, err := d.DecodeBool()
if err != nil {
return n, err
}
ve.SetBool(b)
return n, nil
case reflect.Float32:
f, n, err := d.DecodeFloat()
if err != nil {
return n, err
}
ve.SetFloat(float64(f))
return n, nil
case reflect.Float64:
f, n, err := d.DecodeDouble()
if err != nil {
return n, err
}
ve.SetFloat(f)
return n, nil
case reflect.String:
s, n, err := d.DecodeString()
if err != nil {
return n, err
}
ve.SetString(s)
return n, nil
case reflect.Array:
n, err := d.decodeFixedArray(ve, false)
if err != nil {
return n, err
}
return n, nil
case reflect.Slice:
n, err := d.decodeArray(ve, false)
if err != nil {
return n, err
}
return n, nil
case reflect.Struct:
n, err := d.decodeStruct(ve)
if err != nil {
return n, err
}
return n, nil
case reflect.Map:
n, err := d.decodeMap(ve)
if err != nil {
return n, err
}
return n, nil
case reflect.Interface:
n, err := d.decodeInterface(ve)
if err != nil {
return n, err
}
return n, nil
}
// The only unhandled types left are unsupported. At the time of this
// writing the only remaining unsupported types that exist are
// reflect.Uintptr and reflect.UnsafePointer.
msg := fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String())
err = unmarshalError("decode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// indirect dereferences pointers allocating them as needed until it reaches
// a non-pointer. This allows transparent decoding through arbitrary levels
// of indirection.
func (d *Decoder) indirect(v reflect.Value) (reflect.Value, error) {
rv := v
for rv.Kind() == reflect.Ptr {
// Allocate pointer if needed.
isNil := rv.IsNil()
if isNil && !rv.CanSet() {
msg := fmt.Sprintf("unable to allocate pointer for '%v'",
rv.Type().String())
err := unmarshalError("indirect", ErrNotSettable, msg,
nil, nil)
return rv, err
}
if isNil {
rv.Set(reflect.New(rv.Type().Elem()))
}
rv = rv.Elem()
}
return rv, nil
}
// Decode operates identically to the Unmarshal function with the exception of
// using the reader associated with the Decoder as the source of XDR-encoded
// data instead of a user-supplied reader. See the Unmarhsal documentation for
// specifics.
func (d *Decoder) Decode(v interface{}) (int, error) {
if v == nil {
msg := "can't unmarshal to nil interface"
return 0, unmarshalError("Unmarshal", ErrNilInterface, msg, nil,
nil)
}
vv := reflect.ValueOf(v)
if vv.Kind() != reflect.Ptr {
msg := fmt.Sprintf("can't unmarshal to non-pointer '%v' - use "+
"& operator", vv.Type().String())
err := unmarshalError("Unmarshal", ErrBadArguments, msg, nil, nil)
return 0, err
}
if vv.IsNil() && !vv.CanSet() {
msg := fmt.Sprintf("can't unmarshal to unsettable '%v' - use "+
"& operator", vv.Type().String())
err := unmarshalError("Unmarshal", ErrNotSettable, msg, nil, nil)
return 0, err
}
return d.decode(vv)
}
// NewDecoder returns a Decoder that can be used to manually decode XDR data
// from a provided reader. Typically, Unmarshal should be used instead of
// manually creating a Decoder.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
// NewDecoderLimited is identical to NewDecoder but it sets maxReadSize in
// order to cap reads.
func NewDecoderLimited(r io.Reader, maxSize uint) *Decoder {
return &Decoder{r: r, maxReadSize: maxSize}
}
// NewDecoderCustomTypes returns a decoder with support for custom types known
// to the caller. The second parameter is a map of the type name to the decoder
// routine. When the decoder finds a type matching one of the entries in the map
// it will call the custom routine for that type.
func NewDecoderCustomTypes(r io.Reader, maxSize uint, ct map[string]TypeDecoder) *Decoder {
return &Decoder{r: r, maxReadSize: maxSize, customTypes: ct}
}

View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
Package xdr implements the data representation portion of the External Data
Representation (XDR) standard protocol as specified in RFC 4506 (obsoletes
RFC 1832 and RFC 1014).
The XDR RFC defines both a data specification language and a data
representation standard. This package implements methods to encode and decode
XDR data per the data representation standard with the exception of 128-bit
quadruple-precision floating points. It does not currently implement parsing of
the data specification language. In other words, the ability to automatically
generate Go code by parsing an XDR data specification file (typically .x
extension) is not supported. In practice, this limitation of the package is
fairly minor since it is largely unnecessary due to the reflection capabilities
of Go as described below.
This package provides two approaches for encoding and decoding XDR data:
1) Marshal/Unmarshal functions which automatically map between XDR and Go types
2) Individual Encoder/Decoder objects to manually work with XDR primitives
For the Marshal/Unmarshal functions, Go reflection capabilities are used to
choose the type of the underlying XDR data based upon the Go type to encode or
the target Go type to decode into. A description of how each type is mapped is
provided below, however one important type worth reviewing is Go structs. In
the case of structs, each exported field (first letter capitalized) is reflected
and mapped in order. As a result, this means a Go struct with exported fields
of the appropriate types listed in the expected order can be used to
automatically encode / decode the XDR data thereby eliminating the need to write
a lot of boilerplate code to encode/decode and error check each piece of XDR
data as is typically required with C based XDR libraries.
Go Type to XDR Type Mappings
The following chart shows an overview of how Go types are mapped to XDR types
for automatic marshalling and unmarshalling. The documentation for the Marshal
and Unmarshal functions has specific details of how the mapping proceeds.
Go Type <-> XDR Type
--------------------
int8, int16, int32, int <-> XDR Integer
uint8, uint16, uint32, uint <-> XDR Unsigned Integer
int64 <-> XDR Hyper Integer
uint64 <-> XDR Unsigned Hyper Integer
bool <-> XDR Boolean
float32 <-> XDR Floating-Point
float64 <-> XDR Double-Precision Floating-Point
string <-> XDR String
byte <-> XDR Integer
[]byte <-> XDR Variable-Length Opaque Data
[#]byte <-> XDR Fixed-Length Opaque Data
[]<type> <-> XDR Variable-Length Array
[#]<type> <-> XDR Fixed-Length Array
struct <-> XDR Structure
map <-> XDR Variable-Length Array of two-element XDR Structures
time.Time <-> XDR String encoded with RFC3339 nanosecond precision
Notes and Limitations:
* Automatic marshalling and unmarshalling of variable and fixed-length
arrays of uint8s require a special struct tag `xdropaque:"false"`
since byte slices and byte arrays are assumed to be opaque data and
byte is a Go alias for uint8 thus indistinguishable under reflection
* Channel, complex, and function types cannot be encoded
* Interfaces without a concrete value cannot be encoded
* Cyclic data structures are not supported and will result in infinite
loops
* Strings are marshalled and unmarshalled with UTF-8 character encoding
which differs from the XDR specification of ASCII, however UTF-8 is
backwards compatible with ASCII so this should rarely cause issues
Encoding
To encode XDR data, use the Marshal function.
func Marshal(w io.Writer, v interface{}) (int, error)
For example, given the following code snippet:
type ImageHeader struct {
Signature [3]byte
Version uint32
IsGrayscale bool
NumSections uint32
}
h := ImageHeader{[3]byte{0xAB, 0xCD, 0xEF}, 2, true, 10}
var w bytes.Buffer
bytesWritten, err := xdr.Marshal(&w, &h)
// Error check elided
The result, encodedData, will then contain the following XDR encoded byte
sequence:
0xAB, 0xCD, 0xEF, 0x00,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0A
In addition, while the automatic marshalling discussed above will work for the
vast majority of cases, an Encoder object is provided that can be used to
manually encode XDR primitives for complex scenarios where automatic
reflection-based encoding won't work. The included examples provide a sample of
manual usage via an Encoder.
Decoding
To decode XDR data, use the Unmarshal function.
func Unmarshal(r io.Reader, v interface{}) (int, error)
For example, given the following code snippet:
type ImageHeader struct {
Signature [3]byte
Version uint32
IsGrayscale bool
NumSections uint32
}
// Using output from the Encoding section above.
encodedData := []byte{
0xAB, 0xCD, 0xEF, 0x00,
0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x0A,
}
var h ImageHeader
bytesRead, err := xdr.Unmarshal(bytes.NewReader(encodedData), &h)
// Error check elided
The struct instance, h, will then contain the following values:
h.Signature = [3]byte{0xAB, 0xCD, 0xEF}
h.Version = 2
h.IsGrayscale = true
h.NumSections = 10
In addition, while the automatic unmarshalling discussed above will work for the
vast majority of cases, a Decoder object is provided that can be used to
manually decode XDR primitives for complex scenarios where automatic
reflection-based decoding won't work. The included examples provide a sample of
manual usage via a Decoder.
Errors
All errors are either of type UnmarshalError or MarshalError. Both provide
human-readable output as well as an ErrorCode field which can be inspected by
sophisticated callers if necessary.
See the documentation of UnmarshalError, MarshalError, and ErrorCode for further
details.
*/
package xdr

View file

@ -0,0 +1,669 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package xdr
import (
"fmt"
"io"
"math"
"reflect"
"time"
)
var errIOEncode = "%s while encoding %d bytes"
/*
Marshal writes the XDR encoding of v to writer w and returns the number of bytes
written. It traverses v recursively and automatically indirects pointers
through arbitrary depth to encode the actual value pointed to.
Marshal uses reflection to determine the type of the concrete value contained by
v and performs a mapping of Go types to the underlying XDR types as follows:
Go Type -> XDR Type
--------------------
int8, int16, int32, int -> XDR Integer
uint8, uint16, uint32, uint -> XDR Unsigned Integer
int64 -> XDR Hyper Integer
uint64 -> XDR Unsigned Hyper Integer
bool -> XDR Boolean
float32 -> XDR Floating-Point
float64 -> XDR Double-Precision Floating-Point
string -> XDR String
byte -> XDR Integer
[]byte -> XDR Variable-Length Opaque Data
[#]byte -> XDR Fixed-Length Opaque Data
[]<type> -> XDR Variable-Length Array
[#]<type> -> XDR Fixed-Length Array
struct -> XDR Structure
map -> XDR Variable-Length Array of two-element XDR Structures
time.Time -> XDR String encoded with RFC3339 nanosecond precision
Notes and Limitations:
* Automatic marshalling of variable and fixed-length arrays of uint8s
requires a special struct tag `xdropaque:"false"` since byte slices and
byte arrays are assumed to be opaque data and byte is a Go alias for uint8
thus indistinguishable under reflection
* Channel, complex, and function types cannot be encoded
* Interfaces without a concrete value cannot be encoded
* Cyclic data structures are not supported and will result in infinite loops
* Strings are marshalled with UTF-8 character encoding which differs from
the XDR specification of ASCII, however UTF-8 is backwards compatible with
ASCII so this should rarely cause issues
If any issues are encountered during the marshalling process, a MarshalError is
returned with a human readable description as well as an ErrorCode value for
further inspection from sophisticated callers. Some potential issues are
unsupported Go types, attempting to encode more opaque data than can be
represented by a single opaque XDR entry, and exceeding max slice limitations.
*/
func Marshal(w io.Writer, v interface{}) (int, error) {
enc := Encoder{w: w}
return enc.Encode(v)
}
// An Encoder wraps an io.Writer that will receive the XDR encoded byte stream.
// See NewEncoder.
type Encoder struct {
w io.Writer
}
// EncodeInt writes the XDR encoded representation of the passed 32-bit signed
// integer to the encapsulated writer and returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.1 - Integer
// 32-bit big-endian signed integer in range [-2147483648, 2147483647]
func (enc *Encoder) EncodeInt(v int32) (int, error) {
var b [4]byte
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 4)
err := marshalError("EncodeInt", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeUint writes the XDR encoded representation of the passed 32-bit
// unsigned integer to the encapsulated writer and returns the number of bytes
// written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.2 - Unsigned Integer
// 32-bit big-endian unsigned integer in range [0, 4294967295]
func (enc *Encoder) EncodeUint(v uint32) (int, error) {
var b [4]byte
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 4)
err := marshalError("EncodeUint", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeEnum treats the passed 32-bit signed integer as an enumeration value
// and, if it is in the list of passed valid enumeration values, writes the XDR
// encoded representation of it to the encapsulated writer. It returns the
// number of bytes written.
//
// A MarshalError is returned if the enumeration value is not one of the
// provided valid values or if writing the data fails.
//
// Reference:
// RFC Section 4.3 - Enumeration
// Represented as an XDR encoded signed integer
func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) {
if !validEnums[v] {
err := marshalError("EncodeEnum", ErrBadEnumValue,
"invalid enum", v, nil)
return 0, err
}
return enc.EncodeInt(v)
}
// EncodeBool writes the XDR encoded representation of the passed boolean to the
// encapsulated writer and returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.4 - Boolean
// Represented as an XDR encoded enumeration where 0 is false and 1 is true
func (enc *Encoder) EncodeBool(v bool) (int, error) {
i := int32(0)
if v == true {
i = 1
}
return enc.EncodeInt(i)
}
// EncodeHyper writes the XDR encoded representation of the passed 64-bit
// signed integer to the encapsulated writer and returns the number of bytes
// written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.5 - Hyper Integer
// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807]
func (enc *Encoder) EncodeHyper(v int64) (int, error) {
var b [8]byte
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 8)
err := marshalError("EncodeHyper", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeUhyper writes the XDR encoded representation of the passed 64-bit
// unsigned integer to the encapsulated writer and returns the number of bytes
// written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.5 - Unsigned Hyper Integer
// 64-bit big-endian unsigned integer in range [0, 18446744073709551615]
func (enc *Encoder) EncodeUhyper(v uint64) (int, error) {
var b [8]byte
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
n, err := enc.w.Write(b[:])
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), 8)
err := marshalError("EncodeUhyper", ErrIO, msg, b[:n], err)
return n, err
}
return n, nil
}
// EncodeFloat writes the XDR encoded representation of the passed 32-bit
// (single-precision) floating point to the encapsulated writer and returns the
// number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.6 - Floating Point
// 32-bit single-precision IEEE 754 floating point
func (enc *Encoder) EncodeFloat(v float32) (int, error) {
ui := math.Float32bits(v)
return enc.EncodeUint(ui)
}
// EncodeDouble writes the XDR encoded representation of the passed 64-bit
// (double-precision) floating point to the encapsulated writer and returns the
// number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.7 - Double-Precision Floating Point
// 64-bit double-precision IEEE 754 floating point
func (enc *Encoder) EncodeDouble(v float64) (int, error) {
ui := math.Float64bits(v)
return enc.EncodeUhyper(ui)
}
// RFC Section 4.8 - Quadruple-Precision Floating Point
// 128-bit quadruple-precision floating point
// Not Implemented
// EncodeFixedOpaque treats the passed byte slice as opaque data of a fixed
// size and writes the XDR encoded representation of it to the encapsulated
// writer. It returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.9 - Fixed-Length Opaque Data
// Fixed-length uninterpreted data zero-padded to a multiple of four
func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) {
l := len(v)
pad := (4 - (l % 4)) % 4
// Write the actual bytes.
n, err := enc.w.Write(v)
if err != nil {
msg := fmt.Sprintf(errIOEncode, err.Error(), len(v))
err := marshalError("EncodeFixedOpaque", ErrIO, msg, v[:n], err)
return n, err
}
// Write any padding if needed.
if pad > 0 {
b := make([]byte, pad)
n2, err := enc.w.Write(b)
n += n2
if err != nil {
written := make([]byte, l+n2)
copy(written, v)
copy(written[l:], b[:n2])
msg := fmt.Sprintf(errIOEncode, err.Error(), l+pad)
err := marshalError("EncodeFixedOpaque", ErrIO, msg,
written, err)
return n, err
}
}
return n, nil
}
// EncodeOpaque treats the passed byte slice as opaque data of a variable
// size and writes the XDR encoded representation of it to the encapsulated
// writer. It returns the number of bytes written.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.10 - Variable-Length Opaque Data
// Unsigned integer length followed by fixed opaque data of that length
func (enc *Encoder) EncodeOpaque(v []byte) (int, error) {
// Length of opaque data.
n, err := enc.EncodeUint(uint32(len(v)))
if err != nil {
return n, err
}
n2, err := enc.EncodeFixedOpaque(v)
n += n2
return n, err
}
// EncodeString writes the XDR encoded representation of the passed string
// to the encapsulated writer and returns the number of bytes written.
// Character encoding is assumed to be UTF-8 and therefore ASCII compatible. If
// the underlying character encoding is not compatible with this assumption, the
// data can instead be written as variable-length opaque data (EncodeOpaque) and
// manually converted as needed.
//
// A MarshalError with an error code of ErrIO is returned if writing the data
// fails.
//
// Reference:
// RFC Section 4.11 - String
// Unsigned integer length followed by bytes zero-padded to a multiple of four
func (enc *Encoder) EncodeString(v string) (int, error) {
// Length of string.
n, err := enc.EncodeUint(uint32(len(v)))
if err != nil {
return n, err
}
n2, err := enc.EncodeFixedOpaque([]byte(v))
n += n2
return n, err
}
// encodeFixedArray writes the XDR encoded representation of each element
// in the passed array represented by the reflection value to the encapsulated
// writer and returns the number of bytes written. The ignoreOpaque flag
// controls whether or not uint8 (byte) elements should be encoded individually
// or as a fixed sequence of opaque data.
//
// A MarshalError is returned if any issues are encountered while encoding
// the array elements.
//
// Reference:
// RFC Section 4.12 - Fixed-Length Array
// Individually XDR encoded array elements
func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) {
// Treat [#]byte (byte is alias for uint8) as opaque data unless ignored.
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
// Create a slice of the underlying array for better efficiency
// when possible. Can't create a slice of an unaddressable
// value.
if v.CanAddr() {
return enc.EncodeFixedOpaque(v.Slice(0, v.Len()).Bytes())
}
// When the underlying array isn't addressable fall back to
// copying the array into a new slice. This is rather ugly, but
// the inability to create a constant slice from an
// unaddressable array is a limitation of Go.
slice := make([]byte, v.Len(), v.Len())
reflect.Copy(reflect.ValueOf(slice), v)
return enc.EncodeFixedOpaque(slice)
}
// Encode each array element.
var n int
for i := 0; i < v.Len(); i++ {
n2, err := enc.encode(v.Index(i))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// encodeArray writes an XDR encoded integer representing the number of
// elements in the passed slice represented by the reflection value followed by
// the XDR encoded representation of each element in slice to the encapsulated
// writer and returns the number of bytes written. The ignoreOpaque flag
// controls whether or not uint8 (byte) elements should be encoded individually
// or as a variable sequence of opaque data.
//
// A MarshalError is returned if any issues are encountered while encoding
// the array elements.
//
// Reference:
// RFC Section 4.13 - Variable-Length Array
// Unsigned integer length followed by individually XDR encoded array elements
func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) {
numItems := uint32(v.Len())
n, err := enc.EncodeUint(numItems)
if err != nil {
return n, err
}
n2, err := enc.encodeFixedArray(v, ignoreOpaque)
n += n2
return n, err
}
// encodeStruct writes an XDR encoded representation of each value in the
// exported fields of the struct represented by the passed reflection value to
// the encapsulated writer and returns the number of bytes written. Pointers
// are automatically indirected through arbitrary depth to encode the actual
// value pointed to.
//
// A MarshalError is returned if any issues are encountered while encoding
// the elements.
//
// Reference:
// RFC Section 4.14 - Structure
// XDR encoded elements in the order of their declaration in the struct
func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) {
var n int
vt := v.Type()
for i := 0; i < v.NumField(); i++ {
// Skip unexported fields and indirect through pointers.
vtf := vt.Field(i)
if vtf.PkgPath != "" {
continue
}
vf := v.Field(i)
vf = enc.indirect(vf)
// Handle non-opaque data to []uint8 and [#]uint8 based on struct tag.
tag := vtf.Tag.Get("xdropaque")
if tag == "false" {
switch vf.Kind() {
case reflect.Slice:
n2, err := enc.encodeArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
case reflect.Array:
n2, err := enc.encodeFixedArray(vf, true)
n += n2
if err != nil {
return n, err
}
continue
}
}
// Encode each struct field.
n2, err := enc.encode(vf)
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// RFC Section 4.15 - Discriminated Union
// RFC Section 4.16 - Void
// RFC Section 4.17 - Constant
// RFC Section 4.18 - Typedef
// RFC Section 4.19 - Optional data
// RFC Sections 4.15 though 4.19 only apply to the data specification language
// which is not implemented by this package. In the case of discriminated
// unions, struct tags are used to perform a similar function.
// encodeMap treats the map represented by the passed reflection value as a
// variable-length array of 2-element structures whose fields are of the same
// type as the map keys and elements and writes its XDR encoded representation
// to the encapsulated writer. It returns the number of bytes written.
//
// A MarshalError is returned if any issues are encountered while encoding
// the elements.
func (enc *Encoder) encodeMap(v reflect.Value) (int, error) {
// Number of elements.
n, err := enc.EncodeUint(uint32(v.Len()))
if err != nil {
return n, err
}
// Encode each key and value according to their type.
for _, key := range v.MapKeys() {
n2, err := enc.encode(key)
n += n2
if err != nil {
return n, err
}
n2, err = enc.encode(v.MapIndex(key))
n += n2
if err != nil {
return n, err
}
}
return n, nil
}
// encodeInterface examines the interface represented by the passed reflection
// value to detect whether it is an interface that can be encoded if it is,
// extracts the underlying value to pass back into the encode function for
// encoding according to its type.
//
// A MarshalError is returned if any issues are encountered while encoding
// the interface.
func (enc *Encoder) encodeInterface(v reflect.Value) (int, error) {
if v.IsNil() || !v.CanInterface() {
msg := fmt.Sprintf("can't encode nil interface")
err := marshalError("encodeInterface", ErrNilInterface, msg,
nil, nil)
return 0, err
}
// Extract underlying value from the interface and indirect through pointers.
ve := reflect.ValueOf(v.Interface())
ve = enc.indirect(ve)
return enc.encode(ve)
}
// encode is the main workhorse for marshalling via reflection. It uses
// the passed reflection value to choose the XDR primitives to encode into
// the encapsulated writer and returns the number of bytes written. It is a
// recursive function, so cyclic data structures are not supported and will
// result in an infinite loop.
func (enc *Encoder) encode(v reflect.Value) (int, error) {
if !v.IsValid() {
msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String())
err := marshalError("encode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// Indirect through pointers to get at the concrete value.
ve := enc.indirect(v)
// Handle time.Time values by encoding them as an RFC3339 formatted
// string with nanosecond precision. Check the type string before
// doing a full blown conversion to interface and type assertion since
// checking a string is much quicker.
if ve.Type().String() == "time.Time" && ve.CanInterface() {
viface := ve.Interface()
if tv, ok := viface.(time.Time); ok {
return enc.EncodeString(tv.Format(time.RFC3339Nano))
}
}
// Handle native Go types.
switch ve.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int:
return enc.EncodeInt(int32(ve.Int()))
case reflect.Int64:
return enc.EncodeHyper(ve.Int())
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint:
return enc.EncodeUint(uint32(ve.Uint()))
case reflect.Uint64:
return enc.EncodeUhyper(ve.Uint())
case reflect.Bool:
return enc.EncodeBool(ve.Bool())
case reflect.Float32:
return enc.EncodeFloat(float32(ve.Float()))
case reflect.Float64:
return enc.EncodeDouble(ve.Float())
case reflect.String:
return enc.EncodeString(ve.String())
case reflect.Array:
return enc.encodeFixedArray(ve, false)
case reflect.Slice:
return enc.encodeArray(ve, false)
case reflect.Struct:
return enc.encodeStruct(ve)
case reflect.Map:
return enc.encodeMap(ve)
case reflect.Interface:
return enc.encodeInterface(ve)
}
// The only unhandled types left are unsupported. At the time of this
// writing the only remaining unsupported types that exist are
// reflect.Uintptr and reflect.UnsafePointer.
msg := fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String())
err := marshalError("encode", ErrUnsupportedType, msg, nil, nil)
return 0, err
}
// indirect dereferences pointers until it reaches a non-pointer. This allows
// transparent encoding through arbitrary levels of indirection.
func (enc *Encoder) indirect(v reflect.Value) reflect.Value {
rv := v
for rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
return rv
}
// Encode operates identically to the Marshal function with the exception of
// using the writer associated with the Encoder for the destination of the
// XDR-encoded data instead of a user-supplied writer. See the Marshal
// documentation for specifics.
func (enc *Encoder) Encode(v interface{}) (int, error) {
if v == nil {
msg := "can't marshal nil interface"
err := marshalError("Marshal", ErrNilInterface, msg, nil, nil)
return 0, err
}
vv := reflect.ValueOf(v)
vve := vv
for vve.Kind() == reflect.Ptr {
if vve.IsNil() {
msg := fmt.Sprintf("can't marshal nil pointer '%v'",
vv.Type().String())
err := marshalError("Marshal", ErrBadArguments, msg,
nil, nil)
return 0, err
}
vve = vve.Elem()
}
return enc.encode(vve)
}
// NewEncoder returns an object that can be used to manually choose fields to
// XDR encode to the passed writer w. Typically, Marshal should be used instead
// of manually creating an Encoder. An Encoder, along with several of its
// methods to encode XDR primitives, is exposed so it is possible to perform
// manual encoding of data without relying on reflection should it be necessary
// in complex scenarios where automatic reflection-based encoding won't work.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}

View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package xdr
import "fmt"
// ErrorCode identifies a kind of error.
type ErrorCode int
const (
// ErrBadArguments indicates arguments passed to the function are not
// what was expected.
ErrBadArguments ErrorCode = iota
// ErrUnsupportedType indicates the Go type is not a supported type for
// marshalling and unmarshalling XDR data.
ErrUnsupportedType
// ErrBadEnumValue indicates an enumeration value is not in the list of
// valid values.
ErrBadEnumValue
// ErrNotSettable indicates an interface value cannot be written to.
// This usually means the interface value was not passed with the &
// operator, but it can also happen if automatic pointer allocation
// fails.
ErrNotSettable
// ErrOverflow indicates that the data in question is too large to fit
// into the corresponding Go or XDR data type. For example, an integer
// decoded from XDR that is too large to fit into a target type of int8,
// or opaque data that exceeds the max length of a Go slice.
ErrOverflow
// ErrNilInterface indicates an interface with no concrete type
// information was encountered. Type information is necessary to
// perform mapping between XDR and Go types.
ErrNilInterface
// ErrIO indicates an error was encountered while reading or writing to
// an io.Reader or io.Writer, respectively. The actual underlying error
// will be available via the Err field of the MarshalError or
// UnmarshalError struct.
ErrIO
// ErrParseTime indicates an error was encountered while parsing an
// RFC3339 formatted time value. The actual underlying error will be
// available via the Err field of the UnmarshalError struct.
ErrParseTime
)
// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrBadArguments: "ErrBadArguments",
ErrUnsupportedType: "ErrUnsupportedType",
ErrBadEnumValue: "ErrBadEnumValue",
ErrNotSettable: "ErrNotSettable",
ErrOverflow: "ErrOverflow",
ErrNilInterface: "ErrNilInterface",
ErrIO: "ErrIO",
ErrParseTime: "ErrParseTime",
}
// String returns the ErrorCode as a human-readable name.
func (e ErrorCode) String() string {
if s := errorCodeStrings[e]; s != "" {
return s
}
return fmt.Sprintf("Unknown ErrorCode (%d)", e)
}
// UnmarshalError describes a problem encountered while unmarshaling data.
// Some potential issues are unsupported Go types, attempting to decode a value
// which is too large to fit into a specified Go type, and exceeding max slice
// limitations.
type UnmarshalError struct {
ErrorCode ErrorCode // Describes the kind of error
Func string // Function name
Value interface{} // Value actually parsed where appropriate
Description string // Human readable description of the issue
Err error // The underlying error for IO errors
}
// Error satisfies the error interface and prints human-readable errors.
func (e *UnmarshalError) Error() string {
switch e.ErrorCode {
case ErrBadEnumValue, ErrOverflow, ErrIO, ErrParseTime:
return fmt.Sprintf("xdr:%s: %s - read: '%v'", e.Func,
e.Description, e.Value)
}
return fmt.Sprintf("xdr:%s: %s", e.Func, e.Description)
}
// unmarshalError creates an error given a set of arguments and will copy byte
// slices into the Value field since they might otherwise be changed from from
// the original value.
func unmarshalError(f string, c ErrorCode, desc string, v interface{}, err error) *UnmarshalError {
e := &UnmarshalError{ErrorCode: c, Func: f, Description: desc, Err: err}
switch t := v.(type) {
case []byte:
slice := make([]byte, len(t))
copy(slice, t)
e.Value = slice
default:
e.Value = v
}
return e
}
// IsIO returns a boolean indicating whether the error is known to report that
// the underlying reader or writer encountered an ErrIO.
func IsIO(err error) bool {
switch e := err.(type) {
case *UnmarshalError:
return e.ErrorCode == ErrIO
case *MarshalError:
return e.ErrorCode == ErrIO
}
return false
}
// MarshalError describes a problem encountered while marshaling data.
// Some potential issues are unsupported Go types, attempting to encode more
// opaque data than can be represented by a single opaque XDR entry, and
// exceeding max slice limitations.
type MarshalError struct {
ErrorCode ErrorCode // Describes the kind of error
Func string // Function name
Value interface{} // Value actually parsed where appropriate
Description string // Human readable description of the issue
Err error // The underlying error for IO errors
}
// Error satisfies the error interface and prints human-readable errors.
func (e *MarshalError) Error() string {
switch e.ErrorCode {
case ErrIO:
return fmt.Sprintf("xdr:%s: %s - wrote: '%v'", e.Func,
e.Description, e.Value)
case ErrBadEnumValue:
return fmt.Sprintf("xdr:%s: %s - value: '%v'", e.Func,
e.Description, e.Value)
}
return fmt.Sprintf("xdr:%s: %s", e.Func, e.Description)
}
// marshalError creates an error given a set of arguments and will copy byte
// slices into the Value field since they might otherwise be changed from from
// the original value.
func marshalError(f string, c ErrorCode, desc string, v interface{}, err error) *MarshalError {
e := &MarshalError{ErrorCode: c, Func: f, Description: desc, Err: err}
switch t := v.(type) {
case []byte:
slice := make([]byte, len(t))
copy(slice, t)
e.Value = slice
default:
e.Value = v
}
return e
}