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