| // 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] |
| } |