blob: 56aec3f9ae4b0a4d62c74e997b5f8224b6b74ef7 [file] [log] [blame] [edit]
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fidl
import (
"encoding/binary"
"math"
"reflect"
"syscall/zx"
)
const (
allocPresent uint64 = math.MaxUint64
noAlloc = 0
)
const (
handlePresent uint32 = math.MaxUint32
noHandle = 0
)
const (
maxOutOfLineDepth = 32
)
var (
// TODO(mknyszek): Add support here for process, thread, job, resource,
// interrupt, eventpair, fifo, guest, and time once these are actually
// supported in the Go runtime.
handleType reflect.Type = reflect.TypeOf(zx.Handle(0))
channelType = reflect.TypeOf(zx.Channel(0))
logType = reflect.TypeOf(zx.Log(0))
portType = reflect.TypeOf(zx.Port(0))
vmoType = reflect.TypeOf(zx.VMO(0))
eventType = reflect.TypeOf(zx.Event(0))
socketType = reflect.TypeOf(zx.Socket(0))
vmarType = reflect.TypeOf(zx.VMAR(0))
interfaceRequestType = reflect.TypeOf(InterfaceRequest{})
proxyType = reflect.TypeOf(ChannelProxy{})
)
// isHandleType returns true if the reflected type is a Fuchsia handle type.
func isHandleType(t reflect.Type) bool {
switch t {
case handleType:
fallthrough
case channelType:
fallthrough
case logType:
fallthrough
case portType:
fallthrough
case vmoType:
fallthrough
case eventType:
fallthrough
case socketType:
fallthrough
case vmarType:
return true
}
return false
}
// align increases size such that size is aligned to bytes, and returns the new size.
//
// bytes must be a power of 2.
func align(size, bytes int) int {
alignmentMask := (bytes - 1)
return (size + alignmentMask) & ^alignmentMask
}
// encoder represents the encoding context that is necessary to maintain across
// recursive calls within the same FIDL object.
type encoder struct {
// buffer represents the output buffer that the encoder writes into.
buffer []byte
// handleDispositions are the handle dispositions discovered when traversing
// the FIDL data structure. They are referenced from within the serialized
// data structure in buffer.
handleDispositions []zx.HandleDisposition
}
// Allocate a new out of line object of the specified size for encoding.
// Returns two values: the starting offset of the new object in the buffer and
// an error indicating that a new object could not be allocated.
func (e *encoder) newObject(size, currentDepth int) (int, error) {
if currentDepth > maxOutOfLineDepth {
return 0, newExpectError(ErrExceededMaxOutOfLineDepth, maxOutOfLineDepth, currentDepth+1)
}
size = align(size, 8)
start := len(e.buffer)
e.buffer = append(e.buffer, make([]byte, size)...)
return start, nil
}
// writeUint64 writes a uint64 to the buffer at the next 8-byte aligned position.
func (e *encoder) writeUint64(offset int, val uint64) {
binary.LittleEndian.PutUint64(e.buffer[offset:], val)
}
// writeUint32 writes a uint32 to the buffer at the next 4-byte aligned position.
func (e *encoder) writeUint32(offset int, val uint32) {
binary.LittleEndian.PutUint32(e.buffer[offset:], val)
}
// writeUint16 writes a uint16 to the buffer at the next 2-byte aligned position.
func (e *encoder) writeUint16(offset int, val uint16) {
binary.LittleEndian.PutUint16(e.buffer[offset:], val)
}
// writeUint8 writes a uint8 to the buffer.
func (e *encoder) writeUint8(offset int, val uint8) {
e.buffer[offset] = val
}
// decoder represents the decoding context that is necessary to maintain
// across recursive calls within the same FIDL object.
type decoder struct {
// nextObject is the byte index of the next out-of-line object in buffer.
nextObject int
// buffer represents the buffer we're decoding from.
buffer []byte
// handleInfos represents the input untyped handles we're decoding.
// These objects contain additional information on rights.
handleInfos []zx.HandleInfo
}
// Allocate a new out of line object of the specified size for decoding.
// Returns two values: the starting offset of the new object in the buffer and
// an error indicating that a new object could not be allocated.
func (d *decoder) newObject(size, currentDepth int) (int, error) {
if currentDepth > maxOutOfLineDepth {
return 0, newExpectError(ErrExceededMaxOutOfLineDepth, maxOutOfLineDepth, currentDepth+1)
}
start := d.nextObject
end := start + align(size, 8)
if end > len(d.buffer) {
return 0, ErrPayloadTooSmall
}
for i := d.nextObject + size; i < end; i++ {
if d.buffer[i] != 0 {
return 0, newValueError(ErrNonZeroPadding, d.buffer[i])
}
}
d.nextObject = end
return start, nil
}
// readUint64 reads a uint64 from the buffer.
func (d *decoder) readUint64(offset int) uint64 {
return binary.LittleEndian.Uint64(d.buffer[offset:])
}
// readUint32 reads a uint32 from the buffer.
func (d *decoder) readUint32(offset int) uint32 {
return binary.LittleEndian.Uint32(d.buffer[offset:])
}
// readUint16 reads a uint16 from the buffer.
func (d *decoder) readUint16(offset int) uint16 {
return binary.LittleEndian.Uint16(d.buffer[offset:])
}
// readUint8 reads a uint8 from the buffer.
func (d *decoder) readUint8(offset int) uint8 {
return d.buffer[offset]
}