blob: 1ce64d9e9f5fee54982c0c6180192342828b7c51 [file] [log] [blame]
// Copyright 2015 The Chromium 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 bindings
import (
"fmt"
"mojo/public/go/system"
)
const (
// Flag for a header of a simple message.
MessageNoFlag = 0
// Flag for a header of a message that expected a response.
MessageExpectsResponseFlag = 1 << 0
// Flag for a header of a message that is a response.
MessageIsResponseFlag = 1 << 1
dataHeaderSize = 8
defaultAlignment = 8
pointerBitSize = 64
)
var mapHeader DataHeader
func init() {
mapHeader = DataHeader{24, 0}
}
const (
DifferentSizedArraysInMap = "VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP"
IllegalHandle = "VALIDATION_ERROR_ILLEGAL_HANDLE"
IllegalMemoryRange = "VALIDATION_ERROR_ILLEGAL_MEMORY_RANGE"
IllegalPointer = "VALIDATION_ERROR_ILLEGAL_POINTER"
MessageHeaderInvalidFlags = "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAGS"
MessageHeaderMissingRequestId = "VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID"
MessageHeaderUnknownMethod = "VALIDATION_ERROR_MESSAGE_HEADER_UNKNOWN_METHOD"
MisalignedObject = "VALIDATION_ERROR_MISALIGNED_OBJECT"
UnexpectedArrayHeader = "VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER"
UnexpectedInvalidHandle = "VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE"
UnexpectedNullPointer = "VALIDATION_ERROR_UNEXPECTED_NULL_POINTER"
UnexpectedNullUnion = "VALIDATION_ERROR_UNEXPECTED_NULL_UNION"
UnexpectedStructHeader = "VALIDATION_ERROR_UNEXPECTED_STRUCT_HEADER"
)
// ValidationError is an error that can happen during message validation.
type ValidationError struct {
ErrorCode string
Message string
}
func (e *ValidationError) Error() string {
return e.Message
}
// Payload is an interface implemented by a mojo struct that can encode/decode
// itself into mojo archive format.
type Payload interface {
Encode(encoder *Encoder) error
Decode(decoder *Decoder) error
}
// DataHeader is a header for a mojo complex element.
type DataHeader struct {
Size uint32
ElementsOrVersion uint32
}
// MessageHeader is a header information for a message.
type MessageHeader struct {
Type uint32
Flags uint32
RequestId uint64
}
func (h *MessageHeader) Encode(encoder *Encoder) error {
encoder.StartStruct(h.dataSize(), h.version())
if err := encoder.WriteUint32(h.Type); err != nil {
return err
}
if err := encoder.WriteUint32(h.Flags); err != nil {
return err
}
if h.Flags != MessageNoFlag {
if err := encoder.WriteUint64(h.RequestId); err != nil {
return err
}
}
return encoder.Finish()
}
func (h *MessageHeader) Decode(decoder *Decoder) error {
header, err := decoder.StartStruct()
if err != nil {
return err
}
version := header.ElementsOrVersion
if version > 1 {
return &ValidationError{UnexpectedStructHeader,
fmt.Sprintf("invalid message header: it should be of version 0 or 1, but has %d", version),
}
}
expectedSize := uint32(dataHeaderSize + 2*4)
if version == 1 {
expectedSize += 8
}
if expectedSize != header.Size {
return &ValidationError{UnexpectedStructHeader,
fmt.Sprintf("unexpected struct header size: expected %d, but got %d", expectedSize, header.Size),
}
}
if h.Type, err = decoder.ReadUint32(); err != nil {
return err
}
if h.Flags, err = decoder.ReadUint32(); err != nil {
return err
}
if version == 1 {
if h.Flags != MessageExpectsResponseFlag && h.Flags != MessageIsResponseFlag {
return &ValidationError{MessageHeaderInvalidFlags,
fmt.Sprintf("message header flags(%v) should be MessageExpectsResponseFlag or MessageIsResponseFlag", h.Flags),
}
}
if h.RequestId, err = decoder.ReadUint64(); err != nil {
return err
}
} else {
if h.Flags != MessageNoFlag {
return &ValidationError{MessageHeaderMissingRequestId, "missing request ID in message header"}
}
}
return decoder.Finish()
}
func (h *MessageHeader) dataSize() uint32 {
var size uint32
size = 2 * 4
if h.Flags != MessageNoFlag {
size += 8
}
return size
}
func (h *MessageHeader) version() uint32 {
if h.Flags != MessageNoFlag {
return 1
} else {
return 0
}
}
// Message is a a raw message to be sent/received from a message pipe handle
// which contains a message header.
type Message struct {
Header MessageHeader
Bytes []byte
Handles []system.UntypedHandle
Payload []byte
}
func newMessage(header MessageHeader, bytes []byte, handles []system.UntypedHandle) *Message {
return &Message{header, bytes, handles, bytes[header.dataSize()+dataHeaderSize:]}
}
// DecodePayload decodes the provided payload from the message.
func (m *Message) DecodePayload(payload Payload) error {
decoder := NewDecoder(m.Payload, m.Handles)
if err := payload.Decode(decoder); err != nil {
return err
}
return nil
}
// EncodeMessage returns a message with provided header that has provided
// payload encoded in mojo archive format.
func EncodeMessage(header MessageHeader, payload Payload) (*Message, error) {
encoder := NewEncoder()
if err := header.Encode(encoder); err != nil {
return nil, err
}
if err := payload.Encode(encoder); err != nil {
return nil, err
}
if bytes, handles, err := encoder.Data(); err != nil {
return nil, err
} else {
return newMessage(header, bytes, handles), nil
}
}
// ParseMessage parses message header from byte buffer with attached handles
// and returnes parsed message.
func ParseMessage(bytes []byte, handles []system.UntypedHandle) (*Message, error) {
decoder := NewDecoder(bytes, []system.UntypedHandle{})
var header MessageHeader
if err := header.Decode(decoder); err != nil {
return nil, err
}
return newMessage(header, bytes, handles), nil
}