blob: 7410470bc8acbdd7f6b36ba450c20b71a73a2f1c [file] [log] [blame]
// 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.
// +build fuchsia
package fidl
import (
"math"
"reflect"
"syscall/zx"
)
const (
allocPresent uint64 = math.MaxUint64
noAlloc = 0
)
const (
handlePresent uint32 = math.MaxUint32
noHandle = 0
)
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
}
// isInterfaceType returns true if the reflected type is a FIDL interface type.
func isInterfaceType(t reflect.Type) bool {
// FIDL interfaces are represented as aliases over Proxy.
return t.ConvertibleTo(proxyType)
}
// isInterfaceRequestType returns true if the reflected type is a FIDL interface
// request type.
func isInterfaceRequestType(t reflect.Type) bool {
// FIDL interfaces are represented as aliases over InterfaceRequest.
return t.ConvertibleTo(interfaceRequestType)
}
// 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 {
offset := size & (bytes - 1)
// If we're not currently aligned to |bytes| bytes, add padding.
if offset != 0 {
size += (bytes - offset)
}
return size
}
// encoder represents the encoding context that is necessary to maintain across
// recursive calls within the same FIDL object.
type encoder struct {
// head is the index into buffer at which new data will be written to for the current
// object. It must be updated before writing to a new out-of-line object, and then
// fixed when that object is finished.
head int
// buffer represents the output buffer that the encoder writes into.
buffer []byte
// handles are the handles discovered when traversing the FIDL data
// structure. They are referenced from within the serialized data
// structure in buffer.
handles []zx.Handle
}
func (e *encoder) newObject(size int) int {
size = align(size, 8)
start := len(e.buffer)
e.buffer = append(e.buffer, make([]byte, size)...)
return start
}
// writeUint writes an unsigned integer of byte-width size to the buffer.
//
// Before writing, it pads the buffer such that the integer is aligned to
// its own byte-width.
//
// size must be a power of 2 <= 8.
func (e *encoder) writeUint(val uint64, size int) {
e.head = align(e.head, size)
for i := e.head; i < e.head+size; i++ {
e.buffer[i] = byte(val & 0xFF)
val >>= 8
}
e.head += size
}
// decoder represents the decoding context that is necessary to maintain
// across recursive calls within the same FIDL object.
type decoder struct {
// head is the index into buffer at which new data will be read from for the current
// object. It must be updated before reading from a new out-of-line object, and then
// fixed when that object is finished.
head int
// 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
// handles represents the input untyped handled we're decoding.
handles []zx.Handle
}
// readUint reads an unsigned integer value of byte-width size from the
// buffer, and protects against reading past the end of the buffer.
//
// Before a read, the head is moved forward so as to be naturally aligned with
// the byte-width of the integer it is reading.
//
// Since this is a low-level read, directly off a byte buffer, the only exposed
// shape is `uint64` with the expectation that callers will do the appropriate
// conversion.
//
// size must be a power of 2 <= 8.
func (d *decoder) readUint(size int) (uint64, error) {
d.head = align(d.head, size)
if len(d.buffer) < d.head+size {
return 0, ErrPayloadTooSmall
}
var val uint64
for i := d.head + size - 1; i >= d.head; i-- {
val <<= 8
val |= uint64(d.buffer[i])
}
d.head += size
return val, nil
}