blob: 39f557e034757c43aae3bdd7c101f6efd6e82833 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package memory
import (
"fmt"
"reflect"
"android.googlesource.com/platform/tools/gpu/framework/binary"
"android.googlesource.com/platform/tools/gpu/framework/device"
"android.googlesource.com/platform/tools/gpu/framework/math/u64"
)
func getAlignment(arch device.MemoryLayout, v interface{}) (uint64, error) {
if pt := reflect.TypeOf(Pointer{}); reflect.TypeOf(v).ConvertibleTo(pt) {
return uint64(arch.PointerAlignment), nil
}
t := reflect.TypeOf(v)
switch t.Kind() {
case reflect.Bool, reflect.Int8, reflect.Uint8:
return 1, nil
case reflect.Int16, reflect.Uint16:
return 2, nil
case reflect.Int32, reflect.Uint32, reflect.Float32:
return 4, nil
case reflect.Float64:
return 8, nil
case reflect.Int64, reflect.Uint64:
return uint64(arch.U64Alignment), nil
case reflect.Int, reflect.Uint:
return uint64(arch.IntegerSize), nil
case reflect.Array, reflect.Slice:
return getAlignment(arch, reflect.ValueOf(v).Index(0).Interface())
case reflect.String:
return 1, nil
case reflect.Struct:
value := reflect.ValueOf(v)
alignment := uint64(1)
for i := 0; i < value.NumField(); i++ {
if a, err := getAlignment(arch, value.Field(i).Interface()); err != nil {
return 0, err
} else {
if alignment < a {
alignment = a
}
}
}
return alignment, nil
default:
return 0, fmt.Errorf("alignment calculation for type %v (%v) unimplemented", t, t.Kind())
}
}
// Write writes the value v to the writer w using C alignment rules.
// If v is an array or slice, then each of the elements will be written,
// sequentially. Zeros are used as for paddings. On success, returns
// the number of bytes written and nil, Otherwise, returns 0 and an error.
func Write(w binary.Writer, arch device.MemoryLayout, v interface{}) (uint64, error) {
// <type>ᵖ types are aliases to Pointer. And alias types are different from
// the underlying type in Go. We cannot directly use type assertion/switch
// here to test whether v is essentially of Pointer type.
if pt := reflect.TypeOf(Pointer{}); reflect.TypeOf(v).ConvertibleTo(pt) {
v = reflect.ValueOf(v).Convert(pt).Interface()
binary.WriteUint(w, arch.PointerSize*8, v.(Pointer).Address)
return uint64(arch.PointerSize), w.Error()
}
r := reflect.ValueOf(v)
t := r.Type()
switch t.Kind() {
case reflect.Float32:
w.Float32(float32(r.Float()))
return 4, nil
case reflect.Float64:
w.Float64(r.Float())
return 8, nil
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
binary.WriteInt(w, int32(t.Bits()), r.Int())
return uint64(t.Bits() / 8), nil
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
binary.WriteUint(w, int32(t.Bits()), r.Uint())
return uint64(t.Bits() / 8), nil
case reflect.Int:
binary.WriteInt(w, arch.IntegerSize*8, r.Int())
return uint64(arch.IntegerSize), nil
case reflect.Uint:
binary.WriteUint(w, arch.IntegerSize*8, r.Uint())
return uint64(arch.IntegerSize), nil
case reflect.Array, reflect.Slice:
size := uint64(0)
for i := 0; i < r.Len(); i++ {
element := r.Index(i).Interface()
alignment, err := getAlignment(arch, element)
if err != nil {
return 0, err
}
newSize := u64.AlignUp(size, alignment)
binary.WriteBytes(w, 0, int32(newSize-size))
size = newSize
s, err := Write(w, arch, element)
if err != nil {
return 0, err
}
size += s
}
return size, nil
case reflect.Struct:
size := uint64(0)
for i := 0; i < r.NumField(); i++ {
field := r.Field(i).Interface()
alignment, err := getAlignment(arch, field)
if err != nil {
return 0, err
}
newSize := u64.AlignUp(size, alignment)
binary.WriteBytes(w, 0, int32(newSize-size))
size = newSize
s, err := Write(w, arch, field)
if err != nil {
return 0, err
}
size += s
}
return size, nil
case reflect.String:
s := r.String()
w.String(s)
// Since an unsized string cannot be the sub-element of some composite
// type, the size returned here doesn't really matter regarding as for
// calculating alignments.
// However, the number of bytes written still depends on the writer.
// We assume that the writer null-terminates the string here.
return uint64(len(s)) + 1, nil
case reflect.Bool:
w.Bool(r.Bool())
return 1, nil
default:
return 0, fmt.Errorf("Cannot write type: %s", t.Name())
}
}