blob: 4e7bb69bdc43a41f66591950d85ae799f7044563 [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 fidlgen
import (
"bytes"
"fmt"
)
// In the masks used in the following functions, bytes requiring padding are marked 0xff and
// bytes not requiring padding are marked 0x00.
func (s Struct) populateFullStructMaskForStruct(mask []byte, flatten bool, getTypeShape func(Struct) TypeShape, getFieldShape func(StructMember) FieldShape, resolveStruct func(identifier EncodedCompoundIdentifier) *Struct) {
paddingEnd := getTypeShape(s).InlineSize - 1
for i := len(s.Members) - 1; i >= 0; i-- {
member := s.Members[i]
fieldShape := getFieldShape(member)
if flatten {
s.populateFullStructMaskForType(mask[fieldShape.Offset:paddingEnd+1], &member.Type, flatten, getTypeShape, getFieldShape, resolveStruct)
}
for j := 0; j < fieldShape.Padding; j++ {
mask[paddingEnd-j] = 0xff
}
paddingEnd = fieldShape.Offset - 1
}
}
func (s Struct) populateFullStructMaskForType(mask []byte, typ *Type, flatten bool, getTypeShape func(Struct) TypeShape, getFieldShape func(StructMember) FieldShape, resolveStruct func(identifier EncodedCompoundIdentifier) *Struct) {
if typ.Nullable {
return
}
switch typ.Kind {
case ArrayType:
elemByteSize := len(mask) / *typ.ElementCount
for i := 0; i < *typ.ElementCount; i++ {
s.populateFullStructMaskForType(mask[i*elemByteSize:(i+1)*elemByteSize], typ.ElementType, flatten, getTypeShape, getFieldShape, resolveStruct)
}
case IdentifierType:
sv := resolveStruct(typ.Identifier)
if sv != nil {
sv.populateFullStructMaskForStruct(mask, flatten, getTypeShape, getFieldShape, resolveStruct)
}
}
}
type PaddingMarker struct {
// Offset into the struct (0 is the start of the struct).
Offset int
// Mask, where a 1-bit means the bit in the input value should be zero.
Mask []byte
}
func (s Struct) buildPaddingMarkers(flatten bool, getTypeShape func(Struct) TypeShape, getFieldShape func(StructMember) FieldShape, resolveStruct func(identifier EncodedCompoundIdentifier) *Struct) []PaddingMarker {
var paddingMarkers []PaddingMarker
// Construct a mask across the whole struct with 0xff bytes where there is padding.
fullStructMask := make([]byte, getTypeShape(s).InlineSize)
s.populateFullStructMaskForStruct(fullStructMask, flatten, getTypeShape, getFieldShape, resolveStruct)
// Split up the mask into aligned integer mask segments that can be outputted in the
// fidl_struct! macro.
// Only the sections needing padding are outputted.
// e.g. 00ffff0000ffff000000000000000000 -> 00ffff0000ffff00, 0000000000000000
// -> []PaddingMarker{0, []byte{0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00}}
extractNonzeroSliceOffsets := func(stride int) []int {
var offsets []int
for endi := stride - 1; endi < len(fullStructMask); endi += stride {
i := endi - (stride - 1)
if bytes.Contains(fullStructMask[i:i+stride], []byte{0xff}) {
offsets = append(offsets, i)
}
}
return offsets
}
zeroSlice := func(s []byte) {
for i := range s {
s[i] = 0
}
}
if getTypeShape(s).Alignment >= 8 {
for _, i := range extractNonzeroSliceOffsets(8) {
s := fullStructMask[i : i+8]
m := make([]byte, 8)
copy(m, s)
paddingMarkers = append(paddingMarkers, PaddingMarker{
Offset: i,
Mask: m,
})
zeroSlice(s) // Reset the buffer for the next iteration.
}
}
if getTypeShape(s).Alignment >= 4 {
for _, i := range extractNonzeroSliceOffsets(4) {
s := fullStructMask[i : i+4]
m := make([]byte, 4)
copy(m, s)
paddingMarkers = append(paddingMarkers, PaddingMarker{
Offset: i,
Mask: m,
})
zeroSlice(s) // Reset the buffer for the next iteration.
}
}
for _, i := range extractNonzeroSliceOffsets(2) {
s := fullStructMask[i : i+2]
m := make([]byte, 2)
copy(m, s)
paddingMarkers = append(paddingMarkers, PaddingMarker{
Offset: i,
Mask: m,
})
zeroSlice(s) // Reset the buffer for the next iteration.
}
if bytes.Contains(fullStructMask, []byte{0xff}) {
// This shouldn't be possible because it requires an alignment 1 struct to have padding.
panic(fmt.Sprintf("expected mask to be zero, was %v", fullStructMask))
}
return paddingMarkers
}
type WireFormatVersion int
const (
_ = iota
WireFormatVersionV1
WireFormatVersionV2
)
func getTypeShapeFunc(wireFormatVersion WireFormatVersion) func(Struct) TypeShape {
switch wireFormatVersion {
case WireFormatVersionV1:
return func(s Struct) TypeShape {
return s.TypeShapeV1
}
case WireFormatVersionV2:
return func(s Struct) TypeShape {
return s.TypeShapeV2
}
default:
panic("unknown wire format version")
}
}
func getFieldShapeFunc(wireFormatVersion WireFormatVersion) func(StructMember) FieldShape {
switch wireFormatVersion {
case WireFormatVersionV1:
return func(s StructMember) FieldShape {
return s.FieldShapeV1
}
case WireFormatVersionV2:
return func(s StructMember) FieldShape {
return s.FieldShapeV2
}
default:
panic("unknown wire format version")
}
}
func (s Struct) BuildPaddingMarkers(wireFormatVersion WireFormatVersion) []PaddingMarker {
return s.buildPaddingMarkers(false, getTypeShapeFunc(wireFormatVersion), getFieldShapeFunc(wireFormatVersion), nil)
}
func (s Struct) BuildFlattenedPaddingMarkers(wireFormatVersion WireFormatVersion, resolveStruct func(identifier EncodedCompoundIdentifier) *Struct) []PaddingMarker {
return s.buildPaddingMarkers(true, getTypeShapeFunc(wireFormatVersion), getFieldShapeFunc(wireFormatVersion), resolveStruct)
}