This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch main
in repository ego.
View the commit online.
commit d945d5384dee334fe0c0694cc0c4b7e56f88798f
Author: [email protected] <[email protected]>
AuthorDate: Thu Mar 26 21:27:14 2026 -0600
feat(eet): add struct tag parser and C-compatible layout computation
Implements core infrastructure for mapping Go struct fields to C shadow
buffer offsets with proper alignment. ParseFieldTag handles eet struct
tags (skip, list, inline options), while ComputeLayout and LayoutTotalSize
compute field positions and total buffer sizes respecting C alignment rules.
This foundation enables the descriptor builder to correctly calculate where
each field should read/write in the serialized C data structure.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
eet/eet_test.go | 68 +++++++++++++++++++++++++++++
eet/layout.go | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
eet/tags.go | 33 ++++++++++++++
3 files changed, 231 insertions(+)
diff --git a/eet/eet_test.go b/eet/eet_test.go
index f478a0f..518ca3a 100644
--- a/eet/eet_test.go
+++ b/eet/eet_test.go
@@ -2,6 +2,7 @@ package eet_test
import (
"path/filepath"
+ "reflect"
"testing"
"git.enlightenment.org/cedric/ego/eet"
@@ -52,3 +53,70 @@ func TestOpenFileReadWrite(t *testing.T) {
t.Fatalf("Close: %v", err)
}
}
+
+func TestComputeLayout(t *testing.T) {
+ type Simple struct {
+ A int32 `eet:"a"`
+ B float64 `eet:"b"`
+ C int32 `eet:"c"`
+ }
+
+ fields := eet.ComputeLayout(reflect.TypeFor[Simple]())
+ if len(fields) != 3 {
+ t.Fatalf("got %d fields, want 3", len(fields))
+ }
+ if fields[0].Offset != 0 || fields[0].CSize != 4 {
+ t.Errorf("field A: offset=%d size=%d, want offset=0 size=4", fields[0].Offset, fields[0].CSize)
+ }
+ if fields[1].Offset != 8 || fields[1].CSize != 8 {
+ t.Errorf("field B: offset=%d size=%d, want offset=8 size=8", fields[1].Offset, fields[1].CSize)
+ }
+ if fields[2].Offset != 16 || fields[2].CSize != 4 {
+ t.Errorf("field C: offset=%d size=%d, want offset=16 size=4", fields[2].Offset, fields[2].CSize)
+ }
+}
+
+func TestLayoutTotalSize(t *testing.T) {
+ type Padded struct {
+ A int32 `eet:"a"`
+ B float64 `eet:"b"`
+ }
+ layout := eet.ComputeLayout(reflect.TypeFor[Padded]())
+ total := eet.LayoutTotalSize(layout)
+ if total != 16 {
+ t.Errorf("total size = %d, want 16", total)
+ }
+}
+
+func TestParseTag(t *testing.T) {
+ tests := []struct {
+ tag string
+ wantName string
+ wantSkip bool
+ wantList bool
+ wantInline bool
+ }{
+ {tag: "version", wantName: "version"},
+ {tag: "-", wantSkip: true},
+ {tag: "items,list", wantName: "items", wantList: true},
+ {tag: "counts,array", wantName: "counts"},
+ {tag: "name,inline", wantName: "name", wantInline: true},
+ {tag: "", wantName: ""},
+ }
+
+ for _, tt := range tests {
+ ft := eet.ParseFieldTag(tt.tag)
+ if ft.Name != tt.wantName {
+ t.Errorf("tag %q: name = %q, want %q", tt.tag, ft.Name, tt.wantName)
+ }
+ if ft.Skip != tt.wantSkip {
+ t.Errorf("tag %q: skip = %v, want %v", tt.tag, ft.Skip, tt.wantSkip)
+ }
+ if ft.List != tt.wantList {
+ t.Errorf("tag %q: list = %v, want %v", tt.tag, ft.List, tt.wantList)
+ }
+ if ft.Inline != tt.wantInline {
+ t.Errorf("tag %q: inline = %v, want %v", tt.tag, ft.Inline, tt.wantInline)
+ }
+ }
+}
diff --git a/eet/layout.go b/eet/layout.go
new file mode 100644
index 0000000..6858614
--- /dev/null
+++ b/eet/layout.go
@@ -0,0 +1,130 @@
+package eet
+
+import (
+ "reflect"
+ "strings"
+)
+
+// CField describes a single field's position in the C shadow buffer.
+type CField struct {
+ GoIndex int // Index into the Go struct's fields.
+ Name string // EET field name.
+ Offset int // Byte offset in the C shadow buffer.
+ CSize int // Size in bytes of the C representation.
+ CAlign int // Alignment requirement.
+ Tag FieldTag
+ GoType reflect.Type // The Go type of this field.
+}
+
+// ComputeLayout computes a C-compatible memory layout for the exported,
+// tagged fields of a Go struct type.
+func ComputeLayout(t reflect.Type) []CField {
+ var fields []CField
+ offset := 0
+ for i := 0; i < t.NumField(); i++ {
+ sf := t.Field(i)
+ if !sf.IsExported() {
+ continue
+ }
+ tag := ParseFieldTag(sf.Tag.Get("eet"))
+ if tag.Skip {
+ continue
+ }
+ if tag.Name == "" {
+ tag.Name = strings.ToLower(sf.Name)
+ }
+
+ size, align := cSizeAlign(sf.Type, tag)
+ offset = alignUp(offset, align)
+
+ fields = append(fields, CField{
+ GoIndex: i,
+ Name: tag.Name,
+ Offset: offset,
+ CSize: size,
+ CAlign: align,
+ Tag: tag,
+ GoType: sf.Type,
+ })
+ offset += size
+ }
+ return fields
+}
+
+// LayoutTotalSize returns the total size of the C shadow buffer, padded
+// to the maximum field alignment.
+func LayoutTotalSize(fields []CField) int {
+ if len(fields) == 0 {
+ return 0
+ }
+ maxAlign := 1
+ end := 0
+ for _, f := range fields {
+ if f.CAlign > maxAlign {
+ maxAlign = f.CAlign
+ }
+ if f.Offset+f.CSize > end {
+ end = f.Offset + f.CSize
+ }
+ }
+ return alignUp(end, maxAlign)
+}
+
+// cSizeAlign returns the C size and alignment for a Go type.
+func cSizeAlign(t reflect.Type, tag FieldTag) (size, align int) {
+ switch t.Kind() {
+ case reflect.Bool, reflect.Int8, reflect.Uint8:
+ return 1, 1
+ case reflect.Int16, reflect.Uint16:
+ return 2, 2
+ case reflect.Int32, reflect.Uint32:
+ return 4, 4
+ case reflect.Int, reflect.Uint:
+ return 4, 4
+ case reflect.Int64, reflect.Uint64:
+ return 8, 8
+ case reflect.Float32:
+ return 4, 4
+ case reflect.Float64:
+ return 8, 8
+ case reflect.String:
+ return 8, 8
+ case reflect.Slice:
+ if tag.List {
+ return 8, 8
+ }
+ // VAR_ARRAY: pointer (8) + count int (4), padded to 16.
+ return 16, 8
+ case reflect.Map:
+ return 8, 8
+ case reflect.Struct:
+ sub := ComputeLayout(t)
+ total := LayoutTotalSize(sub)
+ if total == 0 {
+ return 0, 1
+ }
+ return total, maxFieldAlign(sub)
+ case reflect.Pointer:
+ if t.Elem().Kind() == reflect.Struct {
+ return 8, 8
+ }
+ }
+ return 8, 8
+}
+
+func maxFieldAlign(fields []CField) int {
+ m := 1
+ for _, f := range fields {
+ if f.CAlign > m {
+ m = f.CAlign
+ }
+ }
+ return m
+}
+
+func alignUp(offset, align int) int {
+ if align == 0 {
+ return offset
+ }
+ return (offset + align - 1) &^ (align - 1)
+}
diff --git a/eet/tags.go b/eet/tags.go
new file mode 100644
index 0000000..12669b9
--- /dev/null
+++ b/eet/tags.go
@@ -0,0 +1,33 @@
+package eet
+
+import "strings"
+
+// FieldTag holds the parsed contents of an `eet:"..."` struct tag.
+type FieldTag struct {
+ Name string // EET field name.
+ Skip bool // True if tag is "-".
+ List bool // Use Eina_List instead of VAR_ARRAY for slices.
+ Inline bool // Use EET_T_INLINED_STRING for strings.
+}
+
+// ParseFieldTag parses an eet struct tag value.
+func ParseFieldTag(tag string) FieldTag {
+ if tag == "-" {
+ return FieldTag{Skip: true}
+ }
+
+ var ft FieldTag
+ parts := strings.Split(tag, ",")
+ if len(parts) > 0 {
+ ft.Name = parts[0]
+ }
+ for _, opt := range parts[1:] {
+ switch opt {
+ case "list":
+ ft.List = true
+ case "inline":
+ ft.Inline = true
+ }
+ }
+ return ft
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.