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 e7e14f642c8d1965765167ed7930a2b2a5e17f55
Author: [email protected] <[email protected]>
AuthorDate: Thu Mar 26 21:34:25 2026 -0600

    feat(eet): add descriptor builder mapping Go struct types to EET descriptors
    
    This commit introduces the core descriptor bridge between Go reflection and
    EET's C API. The descriptor builder:
    
    - Uses sync.Map for thread-safe caching of computed type descriptors
    - Computes C memory layout matching EET's expectations
    - Creates Eet_Data_Descriptor via eet_data_descriptor_stream_new
    - Registers each struct field via eet_data_descriptor_element_add with
      appropriate EET_T_* type and EET_G_* group constants
    
    The resolveEETType function provides exhaustive type mapping for Go scalar
    types, strings (with inline variant support), slices (arrays/lists), maps
    (hashes), nested structs, and pointers to structs. Tests validate both
    descriptor creation for basic types and caching behavior.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 eet/descriptor.go | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 eet/eet_test.go   |  31 ++++++++
 2 files changed, 238 insertions(+)

diff --git a/eet/descriptor.go b/eet/descriptor.go
new file mode 100644
index 0000000..82d467a
--- /dev/null
+++ b/eet/descriptor.go
@@ -0,0 +1,207 @@
+package eet
+
+/*
+#include <Eet.h>
+#include <stdlib.h>
+
+// _ego_eet_descriptor_new creates a new Eet_Data_Descriptor using the stream
+// class with Eina list/hash callbacks pre-wired.
+static Eet_Data_Descriptor *_ego_eet_descriptor_new(const char *name, int size) {
+    Eet_Data_Descriptor_Class eddc;
+    EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, void);
+    eddc.name = name;
+    eddc.size = size;
+    return eet_data_descriptor_stream_new(&eddc);
+}
+
+// _ego_eet_descriptor_element_add wraps eet_data_descriptor_element_add.
+static void _ego_eet_descriptor_element_add(
+    Eet_Data_Descriptor *edd, const char *name,
+    int type, int group_type, int offset,
+    int count, const char *counter_name,
+    Eet_Data_Descriptor *subtype)
+{
+    eet_data_descriptor_element_add(edd, name, type, group_type,
+        offset, count, counter_name, subtype);
+}
+*/
+import "C"
+import (
+	"fmt"
+	"reflect"
+	"sync"
+	"unsafe"
+)
+
+// typeInfo holds the cached EET descriptor and field layout for a Go struct type.
+type typeInfo struct {
+	desc       *C.Eet_Data_Descriptor
+	shadowSize int
+	fields     []fieldDescriptor
+	err        error
+}
+
+// fieldDescriptor extends CField with EET-specific metadata.
+type fieldDescriptor struct {
+	CField
+	eetType   C.int
+	groupType C.int
+	subInfo   *typeInfo
+}
+
+// Desc returns the underlying EET descriptor pointer.
+func (ti *typeInfo) Desc() unsafe.Pointer {
+	if ti == nil || ti.desc == nil {
+		return nil
+	}
+	return unsafe.Pointer(ti.desc)
+}
+
+// Fields returns the field descriptors.
+func (ti *typeInfo) Fields() []fieldDescriptor {
+	return ti.fields
+}
+
+var typeCache sync.Map // reflect.Type -> *typeInfo
+
+// GetTypeInfo returns the cached typeInfo for a struct type, building it on first access.
+func GetTypeInfo(t reflect.Type) (*typeInfo, error) {
+	if t.Kind() == reflect.Pointer {
+		t = t.Elem()
+	}
+	if t.Kind() != reflect.Struct {
+		return nil, fmt.Errorf("%w: %s is not a struct", ErrUnsupported, t)
+	}
+
+	if v, ok := typeCache.Load(t); ok {
+		ti := v.(*typeInfo)
+		return ti, ti.err
+	}
+
+	ti := buildTypeInfo(t)
+	actual, _ := typeCache.LoadOrStore(t, ti)
+	ti = actual.(*typeInfo)
+	return ti, ti.err
+}
+
+func buildTypeInfo(t reflect.Type) *typeInfo {
+	layout := ComputeLayout(t)
+	totalSize := LayoutTotalSize(layout)
+
+	cName := C.CString(t.Name())
+	defer C.free(unsafe.Pointer(cName))
+
+	desc := C._ego_eet_descriptor_new(cName, C.int(totalSize))
+	if desc == nil {
+		return &typeInfo{err: fmt.Errorf("%w: failed to create descriptor for %s", ErrEncode, t.Name())}
+	}
+
+	ti := &typeInfo{
+		desc:       desc,
+		shadowSize: totalSize,
+	}
+
+	for _, cf := range layout {
+		fd := fieldDescriptor{CField: cf}
+		var err error
+		fd.eetType, fd.groupType, fd.subInfo, err = resolveEETType(cf.GoType, cf.Tag)
+		if err != nil {
+			ti.err = err
+			return ti
+		}
+
+		cFieldName := C.CString(cf.Name)
+
+		var subDesc *C.Eet_Data_Descriptor
+		if fd.subInfo != nil {
+			subDesc = fd.subInfo.desc
+		}
+
+		var counterName *C.char
+		count := C.int(0)
+		if fd.groupType == C.EET_G_VAR_ARRAY {
+			counterName = C.CString(cf.Name + ".count")
+		}
+
+		C._ego_eet_descriptor_element_add(
+			desc, cFieldName,
+			fd.eetType, fd.groupType,
+			C.int(cf.Offset),
+			count, counterName,
+			subDesc,
+		)
+
+		C.free(unsafe.Pointer(cFieldName))
+		if counterName != nil {
+			C.free(unsafe.Pointer(counterName))
+		}
+
+		ti.fields = append(ti.fields, fd)
+	}
+
+	return ti
+}
+
+// resolveEETType maps a Go reflect.Type to EET_T_* and EET_G_* constants.
+func resolveEETType(t reflect.Type, tag FieldTag) (eetType, groupType C.int, sub *typeInfo, err error) {
+	switch t.Kind() {
+	case reflect.Bool:
+		return C.EET_T_UCHAR, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Int8:
+		return C.EET_T_CHAR, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Int16:
+		return C.EET_T_SHORT, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Int, reflect.Int32:
+		return C.EET_T_INT, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Int64:
+		return C.EET_T_LONG_LONG, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Uint8:
+		return C.EET_T_UCHAR, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Uint16:
+		return C.EET_T_USHORT, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Uint, reflect.Uint32:
+		return C.EET_T_UINT, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Uint64:
+		return C.EET_T_ULONG_LONG, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Float32:
+		return C.EET_T_FLOAT, C.EET_G_UNKNOWN, nil, nil
+	case reflect.Float64:
+		return C.EET_T_DOUBLE, C.EET_G_UNKNOWN, nil, nil
+	case reflect.String:
+		if tag.Inline {
+			return C.EET_T_INLINED_STRING, C.EET_G_UNKNOWN, nil, nil
+		}
+		return C.EET_T_STRING, C.EET_G_UNKNOWN, nil, nil
+
+	case reflect.Slice:
+		// For slices of basic types (string, int, etc.), subInfo may be nil
+		// and that's fine — EET handles basic-typed arrays/lists directly.
+		elemSub, _ := GetTypeInfo(t.Elem())
+		if tag.List {
+			return C.EET_T_UNKNOW, C.EET_G_LIST, elemSub, nil
+		}
+		return C.EET_T_UNKNOW, C.EET_G_VAR_ARRAY, elemSub, nil
+
+	case reflect.Map:
+		valSub, _ := GetTypeInfo(t.Elem())
+		return C.EET_T_UNKNOW, C.EET_G_HASH, valSub, nil
+
+	case reflect.Struct:
+		sub, serr := GetTypeInfo(t)
+		if serr != nil {
+			return 0, 0, nil, serr
+		}
+		return C.EET_T_UNKNOW, C.EET_G_UNKNOWN_NESTED, sub, nil
+
+	case reflect.Pointer:
+		if t.Elem().Kind() == reflect.Struct {
+			sub, serr := GetTypeInfo(t.Elem())
+			if serr != nil {
+				return 0, 0, nil, serr
+			}
+			return C.EET_T_UNKNOW, C.EET_G_UNKNOWN, sub, nil
+		}
+	}
+
+	return 0, 0, nil, fmt.Errorf("%w: %s", ErrUnsupported, t)
+}
diff --git a/eet/eet_test.go b/eet/eet_test.go
index 518ca3a..d14c7b4 100644
--- a/eet/eet_test.go
+++ b/eet/eet_test.go
@@ -88,6 +88,37 @@ func TestLayoutTotalSize(t *testing.T) {
 	}
 }
 
+func TestDescriptorBasicTypes(t *testing.T) {
+	type Basic struct {
+		I int     `eet:"i"`
+		F float64 `eet:"f"`
+		S string  `eet:"s"`
+		B bool    `eet:"b"`
+	}
+	info, err := eet.GetTypeInfo(reflect.TypeFor[Basic]())
+	if err != nil {
+		t.Fatalf("GetTypeInfo: %v", err)
+	}
+	if info.Desc() == nil {
+		t.Fatal("descriptor is nil")
+	}
+	if len(info.Fields()) != 4 {
+		t.Fatalf("got %d fields, want 4", len(info.Fields()))
+	}
+}
+
+func TestDescriptorCached(t *testing.T) {
+	type Cached struct {
+		X int `eet:"x"`
+	}
+	typ := reflect.TypeFor[Cached]()
+	a, _ := eet.GetTypeInfo(typ)
+	b, _ := eet.GetTypeInfo(typ)
+	if a != b {
+		t.Fatal("expected same typeInfo pointer for repeated calls")
+	}
+}
+
 func TestParseTag(t *testing.T) {
 	tests := []struct {
 		tag        string

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to