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.

Reply via email to