1047 lines
39 KiB
Go
1047 lines
39 KiB
Go
|
|
package generator
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"sort"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/edmand46/arpack/parser"
|
||
|
|
)
|
||
|
|
|
||
|
|
func collectEnumTypes(enums []parser.Enum) map[string]struct{} {
|
||
|
|
enumTypes := make(map[string]struct{}, len(enums))
|
||
|
|
for _, enum := range enums {
|
||
|
|
if len(enum.Values) == 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
enumTypes[enum.Name] = struct{}{}
|
||
|
|
}
|
||
|
|
return enumTypes
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateCSchema(schema parser.Schema) error {
|
||
|
|
msgIndex := make(map[string]parser.Message, len(schema.Messages))
|
||
|
|
for _, msg := range schema.Messages {
|
||
|
|
msgIndex[msg.Name] = msg
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, msg := range schema.Messages {
|
||
|
|
for _, field := range msg.Fields {
|
||
|
|
if err := validateCField(msg.Name, field, msgIndex, false); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateCField(msgName string, field parser.Field, msgIndex map[string]parser.Message, insideSlice bool) error {
|
||
|
|
switch field.Kind {
|
||
|
|
case parser.KindNested:
|
||
|
|
nested, ok := msgIndex[field.TypeName]
|
||
|
|
if !ok {
|
||
|
|
return fmt.Errorf("c target: unknown nested message %q in %s", field.TypeName, msgName)
|
||
|
|
}
|
||
|
|
if hasNonByteSlices(&nested) {
|
||
|
|
return fmt.Errorf(
|
||
|
|
"c target does not support nested message %s in %s because nested decode contexts are not implemented",
|
||
|
|
field.TypeName,
|
||
|
|
msgName,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
for _, nestedField := range nested.Fields {
|
||
|
|
if err := validateCField(nested.Name, nestedField, msgIndex, false); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
case parser.KindFixedArray:
|
||
|
|
if field.Elem == nil {
|
||
|
|
return fmt.Errorf("c target: fixed array %s in %s has nil element", field.Name, msgName)
|
||
|
|
}
|
||
|
|
if field.Elem.Kind == parser.KindSlice {
|
||
|
|
return fmt.Errorf("c target does not support fixed arrays of slices for field %s in %s", field.Name, msgName)
|
||
|
|
}
|
||
|
|
return validateCField(msgName, *field.Elem, msgIndex, insideSlice)
|
||
|
|
|
||
|
|
case parser.KindSlice:
|
||
|
|
if field.Elem == nil {
|
||
|
|
return fmt.Errorf("c target: slice %s in %s has nil element", field.Name, msgName)
|
||
|
|
}
|
||
|
|
if field.Elem.Kind == parser.KindSlice || field.Elem.Kind == parser.KindFixedArray {
|
||
|
|
return fmt.Errorf(
|
||
|
|
"c target does not support slice element kind %d for field %s in %s",
|
||
|
|
field.Elem.Kind,
|
||
|
|
field.Name,
|
||
|
|
msgName,
|
||
|
|
)
|
||
|
|
}
|
||
|
|
return validateCField(msgName, *field.Elem, msgIndex, true)
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type cSliceViewDef struct {
|
||
|
|
Name string
|
||
|
|
ElemType string
|
||
|
|
}
|
||
|
|
|
||
|
|
func collectSliceViewTypes(messages []parser.Message, baseName string, enumTypes map[string]struct{}) ([]cSliceViewDef, bool) {
|
||
|
|
viewTypes := make(map[string]string)
|
||
|
|
needStringViewSlice := false
|
||
|
|
|
||
|
|
var collectFields func(fields []parser.Field)
|
||
|
|
collectFields = func(fields []parser.Field) {
|
||
|
|
for _, field := range fields {
|
||
|
|
switch field.Kind {
|
||
|
|
case parser.KindSlice:
|
||
|
|
viewType, _ := cFieldTypeInfo(&field, baseName, enumTypes)
|
||
|
|
if viewType == "arpack_string_view_slice_view" {
|
||
|
|
needStringViewSlice = true
|
||
|
|
} else if viewType != "arpack_bytes_view" && viewType != "arpack_string_view_slice_view" {
|
||
|
|
elemType, _ := cFieldTypeInfo(field.Elem, baseName, enumTypes)
|
||
|
|
viewTypes[viewType] = elemType
|
||
|
|
}
|
||
|
|
if field.Elem != nil {
|
||
|
|
collectFields([]parser.Field{*field.Elem})
|
||
|
|
}
|
||
|
|
case parser.KindFixedArray:
|
||
|
|
if field.Elem != nil {
|
||
|
|
collectFields([]parser.Field{*field.Elem})
|
||
|
|
}
|
||
|
|
case parser.KindNested:
|
||
|
|
// Nested messages don't need special handling here
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, msg := range messages {
|
||
|
|
collectFields(msg.Fields)
|
||
|
|
}
|
||
|
|
|
||
|
|
names := make([]string, 0, len(viewTypes))
|
||
|
|
for viewType := range viewTypes {
|
||
|
|
names = append(names, viewType)
|
||
|
|
}
|
||
|
|
sort.Strings(names)
|
||
|
|
result := make([]cSliceViewDef, 0, len(names))
|
||
|
|
for _, name := range names {
|
||
|
|
result = append(result, cSliceViewDef{Name: name, ElemType: viewTypes[name]})
|
||
|
|
}
|
||
|
|
return result, needStringViewSlice
|
||
|
|
}
|
||
|
|
|
||
|
|
func GenerateCSchema(schema parser.Schema, baseName string) (header []byte, source []byte, err error) {
|
||
|
|
if err := validateCSchema(schema); err != nil {
|
||
|
|
return nil, nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
messages := schema.Messages
|
||
|
|
enums := schema.Enums
|
||
|
|
enumTypes := collectEnumTypes(enums)
|
||
|
|
|
||
|
|
var headerBuilder strings.Builder
|
||
|
|
var sourceBuilder strings.Builder
|
||
|
|
|
||
|
|
// Write file headers
|
||
|
|
headerGuard := strings.ToUpper(baseName) + "_GEN_H"
|
||
|
|
headerBuilder.WriteString("// Code generated by arpack. DO NOT EDIT.\n\n")
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf("#ifndef %s\n", headerGuard))
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf("#define %s\n\n", headerGuard))
|
||
|
|
|
||
|
|
sourceBuilder.WriteString("// Code generated by arpack. DO NOT EDIT.\n\n")
|
||
|
|
sourceBuilder.WriteString(fmt.Sprintf("#include \"%s.gen.h\"\n\n", baseName))
|
||
|
|
|
||
|
|
// Write private runtime helpers
|
||
|
|
writeCRuntimeHelpers(&sourceBuilder)
|
||
|
|
|
||
|
|
// Write includes in header
|
||
|
|
headerBuilder.WriteString("#include <stdint.h>\n")
|
||
|
|
headerBuilder.WriteString("#include <stddef.h>\n")
|
||
|
|
headerBuilder.WriteString("#include <stdbool.h>\n\n")
|
||
|
|
|
||
|
|
// Write shared runtime types
|
||
|
|
headerBuilder.WriteString("typedef enum arpack_status {\n")
|
||
|
|
headerBuilder.WriteString(" ARPACK_OK = 0,\n")
|
||
|
|
headerBuilder.WriteString(" ARPACK_ERR_BUFFER_TOO_SHORT = 1,\n")
|
||
|
|
headerBuilder.WriteString(" ARPACK_ERR_LENGTH_OVERFLOW = 2,\n")
|
||
|
|
headerBuilder.WriteString(" ARPACK_ERR_INVALID_ARGUMENT = 3,\n")
|
||
|
|
headerBuilder.WriteString(" ARPACK_ERR_CAPACITY_TOO_SMALL = 4\n")
|
||
|
|
headerBuilder.WriteString("} arpack_status;\n\n")
|
||
|
|
|
||
|
|
headerBuilder.WriteString("typedef struct arpack_string_view {\n")
|
||
|
|
headerBuilder.WriteString(" const char *data;\n")
|
||
|
|
headerBuilder.WriteString(" uint16_t len;\n")
|
||
|
|
headerBuilder.WriteString("} arpack_string_view;\n\n")
|
||
|
|
|
||
|
|
headerBuilder.WriteString("typedef struct arpack_bytes_view {\n")
|
||
|
|
headerBuilder.WriteString(" const uint8_t *data;\n")
|
||
|
|
headerBuilder.WriteString(" uint16_t len;\n")
|
||
|
|
headerBuilder.WriteString("} arpack_bytes_view;\n\n")
|
||
|
|
|
||
|
|
// Collect slice view types needed
|
||
|
|
sliceViewTypes, needStringViewSlice := collectSliceViewTypes(messages, baseName, enumTypes)
|
||
|
|
|
||
|
|
// Forward declare message typedefs (for slice views that reference them)
|
||
|
|
for _, msg := range messages {
|
||
|
|
msgName := baseName + "_" + snakeCase(msg.Name)
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf("typedef struct %s %s;\n", msgName, msgName))
|
||
|
|
}
|
||
|
|
headerBuilder.WriteString("\n")
|
||
|
|
|
||
|
|
// Add arpack_string_view_slice_view if needed
|
||
|
|
if needStringViewSlice {
|
||
|
|
headerBuilder.WriteString("typedef struct arpack_string_view_slice_view {\n")
|
||
|
|
headerBuilder.WriteString(" const arpack_string_view *data;\n")
|
||
|
|
headerBuilder.WriteString(" uint16_t len;\n")
|
||
|
|
headerBuilder.WriteString("} arpack_string_view_slice_view;\n\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Forward declare slice view typedefs
|
||
|
|
for _, viewType := range sliceViewTypes {
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf("typedef struct %s %s;\n", viewType.Name, viewType.Name))
|
||
|
|
}
|
||
|
|
if len(sliceViewTypes) > 0 {
|
||
|
|
headerBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Define slice view types
|
||
|
|
for _, viewType := range sliceViewTypes {
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf("typedef struct %s {\n", viewType.Name))
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf(" const %s *data;\n", viewType.ElemType))
|
||
|
|
headerBuilder.WriteString(" uint16_t len;\n")
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf("} %s;\n\n", viewType.Name))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Write enums
|
||
|
|
for _, enum := range enums {
|
||
|
|
if len(enum.Values) == 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
writeCEnum(&headerBuilder, enum, baseName)
|
||
|
|
headerBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Write message declarations
|
||
|
|
for _, msg := range messages {
|
||
|
|
writeCMessageDecl(&headerBuilder, msg, baseName, enumTypes)
|
||
|
|
headerBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
// Write function declarations for fixed-size messages
|
||
|
|
for _, msg := range messages {
|
||
|
|
if !msg.HasVariableFields() {
|
||
|
|
writeCFixedSizeFuncDecls(&headerBuilder, msg, baseName)
|
||
|
|
headerBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Write decode context declarations for messages with non-byte slices
|
||
|
|
for _, msg := range messages {
|
||
|
|
if hasNonByteSlices(&msg) {
|
||
|
|
writeCDecodeCtxDecl(&headerBuilder, msg, baseName, enumTypes)
|
||
|
|
headerBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Write function declarations for variable-length messages
|
||
|
|
for _, msg := range messages {
|
||
|
|
if msg.HasVariableFields() {
|
||
|
|
writeCVariableSizeFuncDecls(&headerBuilder, msg, baseName)
|
||
|
|
headerBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Close header guard
|
||
|
|
headerBuilder.WriteString(fmt.Sprintf("#endif // %s\n", headerGuard))
|
||
|
|
|
||
|
|
// Write function implementations for fixed-size messages
|
||
|
|
for _, msg := range messages {
|
||
|
|
if !msg.HasVariableFields() {
|
||
|
|
writeCFixedSizeFuncImpls(&sourceBuilder, msg, baseName, enumTypes)
|
||
|
|
sourceBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Write function implementations for variable-length messages
|
||
|
|
for _, msg := range messages {
|
||
|
|
if msg.HasVariableFields() {
|
||
|
|
writeCVariableSizeFuncImpls(&sourceBuilder, msg, baseName, enumTypes)
|
||
|
|
sourceBuilder.WriteString("\n")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return []byte(headerBuilder.String()), []byte(sourceBuilder.String()), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCEnum(b *strings.Builder, enum parser.Enum, baseName string) {
|
||
|
|
enumName := baseName + "_" + snakeCase(enum.Name)
|
||
|
|
b.WriteString(fmt.Sprintf("typedef enum %s {\n", enumName))
|
||
|
|
for _, value := range enum.Values {
|
||
|
|
valueName := baseName + "_" + snakeCase(enum.Name) + "_" + snakeCase(value.Name)
|
||
|
|
b.WriteString(fmt.Sprintf(" %s = %s,\n", valueName, value.Value))
|
||
|
|
}
|
||
|
|
b.WriteString(fmt.Sprintf("} %s;\n", enumName))
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCMessageDecl(b *strings.Builder, msg parser.Message, baseName string, enumTypes map[string]struct{}) {
|
||
|
|
msgName := baseName + "_" + snakeCase(msg.Name)
|
||
|
|
b.WriteString(fmt.Sprintf("typedef struct %s {\n", msgName))
|
||
|
|
for _, field := range msg.Fields {
|
||
|
|
typeStr, arraySuffix := cFieldTypeInfo(&field, baseName, enumTypes)
|
||
|
|
fieldDecl := typeStr + " " + snakeCase(field.Name) + arraySuffix + ";"
|
||
|
|
b.WriteString(fmt.Sprintf(" %s\n", fieldDecl))
|
||
|
|
}
|
||
|
|
b.WriteString(fmt.Sprintf("} %s;\n", msgName))
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCFixedSizeFuncDecls(b *strings.Builder, msg parser.Message, baseName string) {
|
||
|
|
msgName := baseName + "_" + snakeCase(msg.Name)
|
||
|
|
b.WriteString(fmt.Sprintf("size_t %s_min_size(void);\n", msgName))
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_size(const %s *msg, size_t *out_size);\n", msgName, msgName))
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_encode(const %s *msg, uint8_t *buf, size_t buf_len, size_t *out_written);\n", msgName, msgName))
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_decode(%s *msg, const uint8_t *buf, size_t buf_len, size_t *out_read);\n", msgName, msgName))
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCFixedSizeFuncImpls(b *strings.Builder, msg parser.Message, baseName string, enumTypes map[string]struct{}) {
|
||
|
|
msgName := baseName + "_" + snakeCase(msg.Name)
|
||
|
|
minSize := msg.MinWireSize()
|
||
|
|
segs := segmentFields(msg.Fields)
|
||
|
|
|
||
|
|
// min_size function
|
||
|
|
b.WriteString(fmt.Sprintf("size_t %s_min_size(void) {\n", msgName))
|
||
|
|
b.WriteString(fmt.Sprintf(" return %d;\n", minSize))
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// size function (same as min_size for fixed-size messages)
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_size(const %s *msg, size_t *out_size) {\n", msgName, msgName))
|
||
|
|
b.WriteString(fmt.Sprintf(" *out_size = %d;\n", minSize))
|
||
|
|
b.WriteString(" return ARPACK_OK;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// encode function
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_encode(const %s *msg, uint8_t *buf, size_t buf_len, size_t *out_written) {\n", msgName, msgName))
|
||
|
|
b.WriteString(fmt.Sprintf(" if (buf_len < %d) return ARPACK_ERR_BUFFER_TOO_SHORT;\n", minSize))
|
||
|
|
b.WriteString(" size_t offset = 0;\n")
|
||
|
|
|
||
|
|
// Generate encode logic using segments for bool packing
|
||
|
|
for _, seg := range segs {
|
||
|
|
if seg.single != nil {
|
||
|
|
writeCFieldEncode(b, seg.single, "msg->"+snakeCase(seg.single.Name), baseName, enumTypes, " ", 0)
|
||
|
|
} else {
|
||
|
|
// Encode bool group
|
||
|
|
writeCBoolGroupEncode(b, "msg", seg.bools)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
b.WriteString(" *out_written = offset;\n")
|
||
|
|
b.WriteString(" return ARPACK_OK;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// decode function
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_decode(%s *msg, const uint8_t *buf, size_t buf_len, size_t *out_read) {\n", msgName, msgName))
|
||
|
|
b.WriteString(fmt.Sprintf(" if (buf_len < %d) return ARPACK_ERR_BUFFER_TOO_SHORT;\n", minSize))
|
||
|
|
b.WriteString(" size_t offset = 0;\n")
|
||
|
|
|
||
|
|
// Generate decode logic using segments for bool packing
|
||
|
|
for _, seg := range segs {
|
||
|
|
if seg.single != nil {
|
||
|
|
writeCFieldDecode(b, seg.single, "msg->"+snakeCase(seg.single.Name), baseName, enumTypes, "", " ", 0)
|
||
|
|
} else {
|
||
|
|
// Decode bool group
|
||
|
|
writeCBoolGroupDecode(b, "msg", seg.bools)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
b.WriteString(" *out_read = offset;\n")
|
||
|
|
b.WriteString(" return ARPACK_OK;\n")
|
||
|
|
b.WriteString("}\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
func hasNonByteSlices(msg *parser.Message) bool {
|
||
|
|
for _, field := range msg.Fields {
|
||
|
|
if field.Kind == parser.KindSlice {
|
||
|
|
// []uint8 doesn't need context
|
||
|
|
if isRawUint8Element(field.Elem) {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCDecodeCtxDecl(b *strings.Builder, msg parser.Message, baseName string, enumTypes map[string]struct{}) {
|
||
|
|
ctxName := baseName + "_" + snakeCase(msg.Name) + "_decode_ctx"
|
||
|
|
b.WriteString(fmt.Sprintf("typedef struct %s {\n", ctxName))
|
||
|
|
for _, field := range msg.Fields {
|
||
|
|
if field.Kind == parser.KindSlice {
|
||
|
|
// Skip []uint8 - it uses bytes_view which doesn't need context
|
||
|
|
if isRawUint8Element(field.Elem) {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
// Generate context field for non-byte slices
|
||
|
|
fieldName := snakeCase(field.Name)
|
||
|
|
elemType, _ := cFieldTypeInfo(field.Elem, baseName, enumTypes)
|
||
|
|
b.WriteString(fmt.Sprintf(" %s *%s_data;\n", elemType, fieldName))
|
||
|
|
b.WriteString(fmt.Sprintf(" uint16_t %s_cap;\n", fieldName))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
b.WriteString(fmt.Sprintf("} %s;\n", ctxName))
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCVariableSizeFuncDecls(b *strings.Builder, msg parser.Message, baseName string) {
|
||
|
|
msgName := baseName + "_" + snakeCase(msg.Name)
|
||
|
|
b.WriteString(fmt.Sprintf("size_t %s_min_size(void);\n", msgName))
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_size(const %s *msg, size_t *out_size);\n", msgName, msgName))
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_encode(const %s *msg, uint8_t *buf, size_t buf_len, size_t *out_written);\n", msgName, msgName))
|
||
|
|
if hasNonByteSlices(&msg) {
|
||
|
|
ctxName := msgName + "_decode_ctx"
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_decode(%s *msg, const uint8_t *buf, size_t buf_len, %s *ctx, size_t *out_read);\n", msgName, msgName, ctxName))
|
||
|
|
} else {
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_decode(%s *msg, const uint8_t *buf, size_t buf_len, size_t *out_read);\n", msgName, msgName))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCVariableSizeFuncImpls(b *strings.Builder, msg parser.Message, baseName string, enumTypes map[string]struct{}) {
|
||
|
|
msgName := baseName + "_" + snakeCase(msg.Name)
|
||
|
|
minSize := msg.MinWireSize()
|
||
|
|
segs := segmentFields(msg.Fields)
|
||
|
|
hasNonByteSliceFields := hasNonByteSlices(&msg)
|
||
|
|
|
||
|
|
// min_size function
|
||
|
|
b.WriteString(fmt.Sprintf("size_t %s_min_size(void) {\n", msgName))
|
||
|
|
b.WriteString(fmt.Sprintf(" return %d;\n", minSize))
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// size function
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_size(const %s *msg, size_t *out_size) {\n", msgName, msgName))
|
||
|
|
b.WriteString(" size_t size = 0;\n")
|
||
|
|
for _, seg := range segs {
|
||
|
|
if seg.single != nil {
|
||
|
|
writeCFieldSize(b, seg.single, "msg->"+snakeCase(seg.single.Name), baseName, enumTypes, " ", 0)
|
||
|
|
} else {
|
||
|
|
b.WriteString(" size += 1;\n") // bool group is 1 byte
|
||
|
|
}
|
||
|
|
}
|
||
|
|
b.WriteString(" *out_size = size;\n")
|
||
|
|
b.WriteString(" return ARPACK_OK;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// encode function
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_encode(const %s *msg, uint8_t *buf, size_t buf_len, size_t *out_written) {\n", msgName, msgName))
|
||
|
|
b.WriteString(" size_t total_size;\n")
|
||
|
|
b.WriteString(fmt.Sprintf(" arpack_status status = %s_size(msg, &total_size);\n", msgName))
|
||
|
|
b.WriteString(" if (status != ARPACK_OK) return status;\n")
|
||
|
|
b.WriteString(" if (buf_len < total_size) return ARPACK_ERR_BUFFER_TOO_SHORT;\n")
|
||
|
|
b.WriteString(" size_t offset = 0;\n")
|
||
|
|
for _, seg := range segs {
|
||
|
|
if seg.single != nil {
|
||
|
|
writeCFieldEncode(b, seg.single, "msg->"+snakeCase(seg.single.Name), baseName, enumTypes, " ", 0)
|
||
|
|
} else {
|
||
|
|
writeCBoolGroupEncode(b, "msg", seg.bools)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
b.WriteString(" *out_written = offset;\n")
|
||
|
|
b.WriteString(" return ARPACK_OK;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// decode function
|
||
|
|
if hasNonByteSliceFields {
|
||
|
|
ctxName := msgName + "_decode_ctx"
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_decode(%s *msg, const uint8_t *buf, size_t buf_len, %s *ctx, size_t *out_read) {\n", msgName, msgName, ctxName))
|
||
|
|
b.WriteString(" if (ctx == NULL) return ARPACK_ERR_INVALID_ARGUMENT;\n")
|
||
|
|
} else {
|
||
|
|
b.WriteString(fmt.Sprintf("arpack_status %s_decode(%s *msg, const uint8_t *buf, size_t buf_len, size_t *out_read) {\n", msgName, msgName))
|
||
|
|
}
|
||
|
|
b.WriteString(fmt.Sprintf(" if (buf_len < %d) return ARPACK_ERR_BUFFER_TOO_SHORT;\n", minSize))
|
||
|
|
b.WriteString(" size_t offset = 0;\n")
|
||
|
|
for _, seg := range segs {
|
||
|
|
if seg.single != nil {
|
||
|
|
ctxVar := ""
|
||
|
|
if hasNonByteSliceFields {
|
||
|
|
ctxVar = "ctx"
|
||
|
|
}
|
||
|
|
writeCFieldDecode(b, seg.single, "msg->"+snakeCase(seg.single.Name), baseName, enumTypes, ctxVar, " ", 0)
|
||
|
|
} else {
|
||
|
|
writeCBoolGroupDecode(b, "msg", seg.bools)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
b.WriteString(" *out_read = offset;\n")
|
||
|
|
b.WriteString(" return ARPACK_OK;\n")
|
||
|
|
b.WriteString("}\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCFieldSize(
|
||
|
|
b *strings.Builder,
|
||
|
|
field *parser.Field,
|
||
|
|
access string,
|
||
|
|
baseName string,
|
||
|
|
enumTypes map[string]struct{},
|
||
|
|
indent string,
|
||
|
|
depth int,
|
||
|
|
) {
|
||
|
|
switch field.Kind {
|
||
|
|
case parser.KindPrimitive:
|
||
|
|
if field.Quant != nil {
|
||
|
|
b.WriteString(fmt.Sprintf("%ssize += %d;\n", indent, field.Quant.WireBytes()))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if field.Primitive == parser.KindString {
|
||
|
|
b.WriteString(fmt.Sprintf("%ssize += 2 + %s.len;\n", indent, access))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
ws := field.WireSize()
|
||
|
|
if ws > 0 {
|
||
|
|
b.WriteString(fmt.Sprintf("%ssize += %d;\n", indent, ws))
|
||
|
|
}
|
||
|
|
case parser.KindNested:
|
||
|
|
nestedName := baseName + "_" + snakeCase(field.TypeName)
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s size_t nested_size;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s arpack_status status = %s_size(&%s, &nested_size);\n", indent, nestedName, access))
|
||
|
|
b.WriteString(fmt.Sprintf("%s if (status != ARPACK_OK) return status;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s size += nested_size;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
case parser.KindFixedArray:
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %d; %s++) {\n", indent, idxVar, idxVar, field.FixedLen, idxVar))
|
||
|
|
writeCFieldSize(b, field.Elem, access+"["+idxVar+"]", baseName, enumTypes, indent+" ", depth+1)
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
case parser.KindSlice:
|
||
|
|
b.WriteString(fmt.Sprintf("%ssize += 2;\n", indent))
|
||
|
|
if isRawUint8Element(field.Elem) {
|
||
|
|
b.WriteString(fmt.Sprintf("%ssize += %s.len;\n", indent, access))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %s.len; %s++) {\n", indent, idxVar, idxVar, access, idxVar))
|
||
|
|
writeCFieldSize(b, field.Elem, access+".data["+idxVar+"]", baseName, enumTypes, indent+" ", depth+1)
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCBoundsCheck(b *strings.Builder, indent string, needed string) {
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s arpack_status status = _arpack_check_bounds(offset, %s, buf_len);\n", indent, needed))
|
||
|
|
b.WriteString(fmt.Sprintf("%s if (status != ARPACK_OK) return status;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCBoolGroupEncode(b *strings.Builder, msgVar string, bools []parser.Field) {
|
||
|
|
b.WriteString(" {\n")
|
||
|
|
b.WriteString(" uint8_t _boolByte = 0;\n")
|
||
|
|
for i, field := range bools {
|
||
|
|
fieldName := snakeCase(field.Name)
|
||
|
|
access := msgVar + "->" + fieldName
|
||
|
|
b.WriteString(fmt.Sprintf(" if (%s) _boolByte |= (1 << %d);\n", access, i))
|
||
|
|
}
|
||
|
|
b.WriteString(" _arpack_write_u8(buf, &offset, _boolByte);\n")
|
||
|
|
b.WriteString(" }\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCBoolGroupDecode(b *strings.Builder, msgVar string, bools []parser.Field) {
|
||
|
|
b.WriteString(" {\n")
|
||
|
|
writeCBoundsCheck(b, " ", "1")
|
||
|
|
b.WriteString(" uint8_t _boolByte = _arpack_read_u8(buf, &offset);\n")
|
||
|
|
for i, field := range bools {
|
||
|
|
fieldName := snakeCase(field.Name)
|
||
|
|
access := msgVar + "->" + fieldName
|
||
|
|
b.WriteString(fmt.Sprintf(" %s = (_boolByte & (1 << %d)) != 0;\n", access, i))
|
||
|
|
}
|
||
|
|
b.WriteString(" }\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCFieldEncode(
|
||
|
|
b *strings.Builder,
|
||
|
|
field *parser.Field,
|
||
|
|
access string,
|
||
|
|
baseName string,
|
||
|
|
enumTypes map[string]struct{},
|
||
|
|
indent string,
|
||
|
|
depth int,
|
||
|
|
) {
|
||
|
|
switch field.Kind {
|
||
|
|
case parser.KindPrimitive:
|
||
|
|
if field.Quant != nil && (field.Primitive == parser.KindFloat32 || field.Primitive == parser.KindFloat64) {
|
||
|
|
writeCQuantizedEncode(b, field, access, indent)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if field.Primitive == parser.KindString {
|
||
|
|
b.WriteString(fmt.Sprintf("%s_arpack_write_u16_le(buf, &offset, %s.len);\n", indent, access))
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %s.len; %s++) {\n", indent, idxVar, idxVar, access, idxVar))
|
||
|
|
b.WriteString(fmt.Sprintf("%s _arpack_write_u8(buf, &offset, (uint8_t)%s.data[%s]);\n", indent, access, idxVar))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
helper := cPrimitiveWriteHelper(field.Primitive)
|
||
|
|
if field.Primitive == parser.KindBool {
|
||
|
|
b.WriteString(fmt.Sprintf("%s_arpack_write_%s(buf, &offset, %s ? 1 : 0);\n", indent, helper, access))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
b.WriteString(fmt.Sprintf("%s_arpack_write_%s(buf, &offset, %s);\n", indent, helper, access))
|
||
|
|
case parser.KindNested:
|
||
|
|
nestedName := baseName + "_" + snakeCase(field.TypeName)
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s size_t nested_written;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s arpack_status status = %s_encode(&%s, buf + offset, buf_len - offset, &nested_written);\n", indent, nestedName, access))
|
||
|
|
b.WriteString(fmt.Sprintf("%s if (status != ARPACK_OK) return status;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s offset += nested_written;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
case parser.KindFixedArray:
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %d; %s++) {\n", indent, idxVar, idxVar, field.FixedLen, idxVar))
|
||
|
|
writeCFieldEncode(b, field.Elem, access+"["+idxVar+"]", baseName, enumTypes, indent+" ", depth+1)
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
case parser.KindSlice:
|
||
|
|
b.WriteString(fmt.Sprintf("%s_arpack_write_u16_le(buf, &offset, %s.len);\n", indent, access))
|
||
|
|
if isRawUint8Element(field.Elem) {
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %s.len; %s++) {\n", indent, idxVar, idxVar, access, idxVar))
|
||
|
|
b.WriteString(fmt.Sprintf("%s _arpack_write_u8(buf, &offset, %s.data[%s]);\n", indent, access, idxVar))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %s.len; %s++) {\n", indent, idxVar, idxVar, access, idxVar))
|
||
|
|
writeCFieldEncode(b, field.Elem, access+".data["+idxVar+"]", baseName, enumTypes, indent+" ", depth+1)
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCQuantizedEncode(b *strings.Builder, field *parser.Field, access string, indent string) {
|
||
|
|
maxUint := int(field.Quant.MaxUint())
|
||
|
|
if field.Quant.Bits == 8 {
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s double _q = ((double)%s - (%g)) / ((%g) - (%g)) * %d;\n", indent, access, field.Quant.Min, field.Quant.Max, field.Quant.Min, maxUint))
|
||
|
|
b.WriteString(fmt.Sprintf("%s if (_q < 0.0 || _q > %d.0) return ARPACK_ERR_INVALID_ARGUMENT;\n", indent, maxUint))
|
||
|
|
b.WriteString(fmt.Sprintf("%s uint8_t _qv = (uint8_t)_q;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s _arpack_write_u8(buf, &offset, _qv);\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s double _q = ((double)%s - (%g)) / ((%g) - (%g)) * %d;\n", indent, access, field.Quant.Min, field.Quant.Max, field.Quant.Min, maxUint))
|
||
|
|
b.WriteString(fmt.Sprintf("%s if (_q < 0.0 || _q > %d.0) return ARPACK_ERR_INVALID_ARGUMENT;\n", indent, maxUint))
|
||
|
|
b.WriteString(fmt.Sprintf("%s uint16_t _qv = (uint16_t)_q;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s _arpack_write_u16_le(buf, &offset, _qv);\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCFieldDecode(
|
||
|
|
b *strings.Builder,
|
||
|
|
field *parser.Field,
|
||
|
|
access string,
|
||
|
|
baseName string,
|
||
|
|
enumTypes map[string]struct{},
|
||
|
|
ctxVar string,
|
||
|
|
indent string,
|
||
|
|
depth int,
|
||
|
|
) {
|
||
|
|
switch field.Kind {
|
||
|
|
case parser.KindPrimitive:
|
||
|
|
if field.Quant != nil && (field.Primitive == parser.KindFloat32 || field.Primitive == parser.KindFloat64) {
|
||
|
|
writeCQuantizedDecode(b, field, access, indent)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if field.Primitive == parser.KindString {
|
||
|
|
writeCBoundsCheck(b, indent, "2")
|
||
|
|
b.WriteString(fmt.Sprintf("%s%s.len = _arpack_read_u16_le(buf, &offset);\n", indent, access))
|
||
|
|
writeCBoundsCheck(b, indent, access+".len")
|
||
|
|
b.WriteString(fmt.Sprintf("%s%s.data = (const char *)(buf + offset);\n", indent, access))
|
||
|
|
b.WriteString(fmt.Sprintf("%soffset += %s.len;\n", indent, access))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
writeCBoundsCheck(b, indent, fmt.Sprintf("%d", field.WireSize()))
|
||
|
|
helper := cPrimitiveReadHelper(field.Primitive)
|
||
|
|
if field.Primitive == parser.KindBool {
|
||
|
|
b.WriteString(fmt.Sprintf("%s%s = _arpack_read_%s(buf, &offset) != 0;\n", indent, access, helper))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
b.WriteString(fmt.Sprintf("%s%s = _arpack_read_%s(buf, &offset);\n", indent, access, helper))
|
||
|
|
case parser.KindNested:
|
||
|
|
nestedName := baseName + "_" + snakeCase(field.TypeName)
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s size_t nested_read;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s arpack_status status = %s_decode(&%s, buf + offset, buf_len - offset, &nested_read);\n", indent, nestedName, access))
|
||
|
|
b.WriteString(fmt.Sprintf("%s if (status != ARPACK_OK) return status;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s offset += nested_read;\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
case parser.KindFixedArray:
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %d; %s++) {\n", indent, idxVar, idxVar, field.FixedLen, idxVar))
|
||
|
|
writeCFieldDecode(b, field.Elem, access+"["+idxVar+"]", baseName, enumTypes, "", indent+" ", depth+1)
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
case parser.KindSlice:
|
||
|
|
writeCBoundsCheck(b, indent, "2")
|
||
|
|
b.WriteString(fmt.Sprintf("%s%s.len = _arpack_read_u16_le(buf, &offset);\n", indent, access))
|
||
|
|
if isRawUint8Element(field.Elem) {
|
||
|
|
writeCBoundsCheck(b, indent, access+".len")
|
||
|
|
b.WriteString(fmt.Sprintf("%s%s.data = buf + offset;\n", indent, access))
|
||
|
|
b.WriteString(fmt.Sprintf("%soffset += %s.len;\n", indent, access))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
ctxField := snakeCase(field.Name)
|
||
|
|
b.WriteString(fmt.Sprintf("%sif (%s.len > %s->%s_cap) return ARPACK_ERR_CAPACITY_TOO_SMALL;\n", indent, access, ctxVar, ctxField))
|
||
|
|
b.WriteString(fmt.Sprintf("%s%s.data = %s->%s_data;\n", indent, access, ctxVar, ctxField))
|
||
|
|
idxVar := fmt.Sprintf("_i%d", depth)
|
||
|
|
b.WriteString(fmt.Sprintf("%sfor (uint16_t %s = 0; %s < %s.len; %s++) {\n", indent, idxVar, idxVar, access, idxVar))
|
||
|
|
writeCFieldDecode(b, field.Elem, ctxVar+"->"+ctxField+"_data["+idxVar+"]", baseName, enumTypes, "", indent+" ", depth+1)
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCQuantizedDecode(b *strings.Builder, field *parser.Field, access string, indent string) {
|
||
|
|
maxUint := field.Quant.MaxUint()
|
||
|
|
if field.Quant.Bits == 8 {
|
||
|
|
writeCBoundsCheck(b, indent, "1")
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s uint8_t _qv = _arpack_read_u8(buf, &offset);\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s %s = ((double)_qv / %g) * ((%g) - (%g)) + (%g);\n", indent, access, maxUint, field.Quant.Max, field.Quant.Min, field.Quant.Min))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
writeCBoundsCheck(b, indent, "2")
|
||
|
|
b.WriteString(fmt.Sprintf("%s{\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s uint16_t _qv = _arpack_read_u16_le(buf, &offset);\n", indent))
|
||
|
|
b.WriteString(fmt.Sprintf("%s %s = ((double)_qv / %g) * ((%g) - (%g)) + (%g);\n", indent, access, maxUint, field.Quant.Max, field.Quant.Min, field.Quant.Min))
|
||
|
|
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||
|
|
}
|
||
|
|
|
||
|
|
func cFieldTypeInfo(field *parser.Field, baseName string, enumTypes map[string]struct{}) (typeStr string, arraySuffix string) {
|
||
|
|
switch field.Kind {
|
||
|
|
case parser.KindPrimitive:
|
||
|
|
return cPrimitiveTypeInfo(field, baseName, enumTypes), ""
|
||
|
|
case parser.KindNested:
|
||
|
|
return baseName + "_" + snakeCase(field.TypeName), ""
|
||
|
|
case parser.KindFixedArray:
|
||
|
|
elemType, elemSuffix := cFieldTypeInfo(field.Elem, baseName, enumTypes)
|
||
|
|
return elemType, fmt.Sprintf("[%d]%s", field.FixedLen, elemSuffix)
|
||
|
|
case parser.KindSlice:
|
||
|
|
if isRawUint8Element(field.Elem) {
|
||
|
|
return "arpack_bytes_view", ""
|
||
|
|
}
|
||
|
|
if field.Elem.Kind == parser.KindPrimitive && field.Elem.Primitive == parser.KindString {
|
||
|
|
return "arpack_string_view_slice_view", ""
|
||
|
|
}
|
||
|
|
return cSliceViewTypeName(field.Elem, baseName, enumTypes), ""
|
||
|
|
default:
|
||
|
|
return "void*", ""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func cFieldType(field *parser.Field, baseName string, enumTypes map[string]struct{}) string {
|
||
|
|
typeStr, suffix := cFieldTypeInfo(field, baseName, enumTypes)
|
||
|
|
if suffix != "" {
|
||
|
|
return typeStr + suffix
|
||
|
|
}
|
||
|
|
return typeStr
|
||
|
|
}
|
||
|
|
|
||
|
|
func cSliceViewTypeName(field *parser.Field, baseName string, enumTypes map[string]struct{}) string {
|
||
|
|
return baseName + "_" + cSliceViewElemKey(field, enumTypes) + "_slice_view"
|
||
|
|
}
|
||
|
|
|
||
|
|
func cSliceViewElemKey(field *parser.Field, enumTypes map[string]struct{}) string {
|
||
|
|
switch field.Kind {
|
||
|
|
case parser.KindPrimitive:
|
||
|
|
if field.NamedType != "" {
|
||
|
|
if _, ok := enumTypes[field.NamedType]; ok {
|
||
|
|
return snakeCase(field.NamedType)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return cPrimitiveTypeToken(field.Primitive)
|
||
|
|
case parser.KindNested:
|
||
|
|
return snakeCase(field.TypeName)
|
||
|
|
default:
|
||
|
|
return "unsupported"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func cPrimitiveTypeInfo(field *parser.Field, baseName string, enumTypes map[string]struct{}) string {
|
||
|
|
if field.NamedType != "" {
|
||
|
|
if _, ok := enumTypes[field.NamedType]; ok {
|
||
|
|
return baseName + "_" + snakeCase(field.NamedType)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
switch field.Primitive {
|
||
|
|
case parser.KindInt8:
|
||
|
|
return "int8_t"
|
||
|
|
case parser.KindInt16:
|
||
|
|
return "int16_t"
|
||
|
|
case parser.KindInt32:
|
||
|
|
return "int32_t"
|
||
|
|
case parser.KindInt64:
|
||
|
|
return "int64_t"
|
||
|
|
case parser.KindUint8:
|
||
|
|
return "uint8_t"
|
||
|
|
case parser.KindUint16:
|
||
|
|
return "uint16_t"
|
||
|
|
case parser.KindUint32:
|
||
|
|
return "uint32_t"
|
||
|
|
case parser.KindUint64:
|
||
|
|
return "uint64_t"
|
||
|
|
case parser.KindFloat32:
|
||
|
|
return "float"
|
||
|
|
case parser.KindFloat64:
|
||
|
|
return "double"
|
||
|
|
case parser.KindBool:
|
||
|
|
return "bool"
|
||
|
|
case parser.KindString:
|
||
|
|
return "arpack_string_view"
|
||
|
|
default:
|
||
|
|
return "void*"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func cPrimitiveTypeToken(k parser.PrimitiveKind) string {
|
||
|
|
switch k {
|
||
|
|
case parser.KindInt8:
|
||
|
|
return "int8"
|
||
|
|
case parser.KindInt16:
|
||
|
|
return "int16"
|
||
|
|
case parser.KindInt32:
|
||
|
|
return "int32"
|
||
|
|
case parser.KindInt64:
|
||
|
|
return "int64"
|
||
|
|
case parser.KindUint8:
|
||
|
|
return "uint8"
|
||
|
|
case parser.KindUint16:
|
||
|
|
return "uint16"
|
||
|
|
case parser.KindUint32:
|
||
|
|
return "uint32"
|
||
|
|
case parser.KindUint64:
|
||
|
|
return "uint64"
|
||
|
|
case parser.KindFloat32:
|
||
|
|
return "float32"
|
||
|
|
case parser.KindFloat64:
|
||
|
|
return "float64"
|
||
|
|
case parser.KindBool:
|
||
|
|
return "bool"
|
||
|
|
case parser.KindString:
|
||
|
|
return "string"
|
||
|
|
default:
|
||
|
|
return "unknown"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func cPrimitiveReadHelper(k parser.PrimitiveKind) string {
|
||
|
|
switch k {
|
||
|
|
case parser.KindInt8:
|
||
|
|
return "i8"
|
||
|
|
case parser.KindInt16:
|
||
|
|
return "i16_le"
|
||
|
|
case parser.KindInt32:
|
||
|
|
return "i32_le"
|
||
|
|
case parser.KindInt64:
|
||
|
|
return "i64_le"
|
||
|
|
case parser.KindUint8, parser.KindBool:
|
||
|
|
return "u8"
|
||
|
|
case parser.KindUint16:
|
||
|
|
return "u16_le"
|
||
|
|
case parser.KindUint32:
|
||
|
|
return "u32_le"
|
||
|
|
case parser.KindUint64:
|
||
|
|
return "u64_le"
|
||
|
|
case parser.KindFloat32:
|
||
|
|
return "f32_le"
|
||
|
|
case parser.KindFloat64:
|
||
|
|
return "f64_le"
|
||
|
|
default:
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func cPrimitiveWriteHelper(k parser.PrimitiveKind) string {
|
||
|
|
return cPrimitiveReadHelper(k)
|
||
|
|
}
|
||
|
|
|
||
|
|
func isRawUint8Element(field *parser.Field) bool {
|
||
|
|
return field != nil &&
|
||
|
|
field.Kind == parser.KindPrimitive &&
|
||
|
|
field.Primitive == parser.KindUint8 &&
|
||
|
|
field.NamedType == "" &&
|
||
|
|
field.Quant == nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeCRuntimeHelpers(b *strings.Builder) {
|
||
|
|
b.WriteString("// Private runtime helpers\n\n")
|
||
|
|
|
||
|
|
// Bounds check helper
|
||
|
|
b.WriteString("static inline arpack_status _arpack_check_bounds(size_t offset, size_t needed, size_t buf_len) {\n")
|
||
|
|
b.WriteString(" if (offset + needed > buf_len) return ARPACK_ERR_BUFFER_TOO_SHORT;\n")
|
||
|
|
b.WriteString(" return ARPACK_OK;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// Read helpers (little-endian)
|
||
|
|
b.WriteString("static inline uint8_t _arpack_read_u8(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" uint8_t val = buf[*offset];\n")
|
||
|
|
b.WriteString(" *offset += 1;\n")
|
||
|
|
b.WriteString(" return val;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline uint16_t _arpack_read_u16_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" uint16_t val = (uint16_t)buf[*offset] | ((uint16_t)buf[*offset + 1] << 8);\n")
|
||
|
|
b.WriteString(" *offset += 2;\n")
|
||
|
|
b.WriteString(" return val;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline uint32_t _arpack_read_u32_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" uint32_t val = (uint32_t)buf[*offset] | ((uint32_t)buf[*offset + 1] << 8) |\\\n")
|
||
|
|
b.WriteString(" ((uint32_t)buf[*offset + 2] << 16) | ((uint32_t)buf[*offset + 3] << 24);\n")
|
||
|
|
b.WriteString(" *offset += 4;\n")
|
||
|
|
b.WriteString(" return val;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline uint64_t _arpack_read_u64_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" uint64_t val = (uint64_t)buf[*offset] | ((uint64_t)buf[*offset + 1] << 8) |\\\n")
|
||
|
|
b.WriteString(" ((uint64_t)buf[*offset + 2] << 16) | ((uint64_t)buf[*offset + 3] << 24) |\\\n")
|
||
|
|
b.WriteString(" ((uint64_t)buf[*offset + 4] << 32) | ((uint64_t)buf[*offset + 5] << 40) |\\\n")
|
||
|
|
b.WriteString(" ((uint64_t)buf[*offset + 6] << 48) | ((uint64_t)buf[*offset + 7] << 56);\n")
|
||
|
|
b.WriteString(" *offset += 8;\n")
|
||
|
|
b.WriteString(" return val;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline int8_t _arpack_read_i8(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" return (int8_t)_arpack_read_u8(buf, offset);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline int16_t _arpack_read_i16_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" return (int16_t)_arpack_read_u16_le(buf, offset);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline int32_t _arpack_read_i32_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" return (int32_t)_arpack_read_u32_le(buf, offset);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline int64_t _arpack_read_i64_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" return (int64_t)_arpack_read_u64_le(buf, offset);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// Write helpers (little-endian)
|
||
|
|
b.WriteString("static inline void _arpack_write_u8(uint8_t *buf, size_t *offset, uint8_t val) {\n")
|
||
|
|
b.WriteString(" buf[*offset] = val;\n")
|
||
|
|
b.WriteString(" *offset += 1;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_u16_le(uint8_t *buf, size_t *offset, uint16_t val) {\n")
|
||
|
|
b.WriteString(" buf[*offset] = val & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 1] = (val >> 8) & 0xFF;\n")
|
||
|
|
b.WriteString(" *offset += 2;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_u32_le(uint8_t *buf, size_t *offset, uint32_t val) {\n")
|
||
|
|
b.WriteString(" buf[*offset] = val & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 1] = (val >> 8) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 2] = (val >> 16) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 3] = (val >> 24) & 0xFF;\n")
|
||
|
|
b.WriteString(" *offset += 4;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_u64_le(uint8_t *buf, size_t *offset, uint64_t val) {\n")
|
||
|
|
b.WriteString(" buf[*offset] = val & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 1] = (val >> 8) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 2] = (val >> 16) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 3] = (val >> 24) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 4] = (val >> 32) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 5] = (val >> 40) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 6] = (val >> 48) & 0xFF;\n")
|
||
|
|
b.WriteString(" buf[*offset + 7] = (val >> 56) & 0xFF;\n")
|
||
|
|
b.WriteString(" *offset += 8;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_i8(uint8_t *buf, size_t *offset, int8_t val) {\n")
|
||
|
|
b.WriteString(" _arpack_write_u8(buf, offset, (uint8_t)val);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_i16_le(uint8_t *buf, size_t *offset, int16_t val) {\n")
|
||
|
|
b.WriteString(" _arpack_write_u16_le(buf, offset, (uint16_t)val);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_i32_le(uint8_t *buf, size_t *offset, int32_t val) {\n")
|
||
|
|
b.WriteString(" _arpack_write_u32_le(buf, offset, (uint32_t)val);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_i64_le(uint8_t *buf, size_t *offset, int64_t val) {\n")
|
||
|
|
b.WriteString(" _arpack_write_u64_le(buf, offset, (uint64_t)val);\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// Float bit conversion helpers
|
||
|
|
b.WriteString("static inline float _arpack_u32_to_f(uint32_t bits) {\n")
|
||
|
|
b.WriteString(" union { uint32_t u; float f; } conv;\n")
|
||
|
|
b.WriteString(" conv.u = bits;\n")
|
||
|
|
b.WriteString(" return conv.f;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline uint32_t _arpack_f_to_u32(float val) {\n")
|
||
|
|
b.WriteString(" union { uint32_t u; float f; } conv;\n")
|
||
|
|
b.WriteString(" conv.f = val;\n")
|
||
|
|
b.WriteString(" return conv.u;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline double _arpack_u64_to_f(uint64_t bits) {\n")
|
||
|
|
b.WriteString(" union { uint64_t u; double f; } conv;\n")
|
||
|
|
b.WriteString(" conv.u = bits;\n")
|
||
|
|
b.WriteString(" return conv.f;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline uint64_t _arpack_f_to_u64(double val) {\n")
|
||
|
|
b.WriteString(" union { uint64_t u; double f; } conv;\n")
|
||
|
|
b.WriteString(" conv.f = val;\n")
|
||
|
|
b.WriteString(" return conv.u;\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
// Float read/write helpers
|
||
|
|
b.WriteString("static inline float _arpack_read_f32_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" return _arpack_u32_to_f(_arpack_read_u32_le(buf, offset));\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline double _arpack_read_f64_le(const uint8_t *buf, size_t *offset) {\n")
|
||
|
|
b.WriteString(" return _arpack_u64_to_f(_arpack_read_u64_le(buf, offset));\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_f32_le(uint8_t *buf, size_t *offset, float val) {\n")
|
||
|
|
b.WriteString(" _arpack_write_u32_le(buf, offset, _arpack_f_to_u32(val));\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
|
||
|
|
b.WriteString("static inline void _arpack_write_f64_le(uint8_t *buf, size_t *offset, double val) {\n")
|
||
|
|
b.WriteString(" _arpack_write_u64_le(buf, offset, _arpack_f_to_u64(val));\n")
|
||
|
|
b.WriteString("}\n\n")
|
||
|
|
}
|
||
|
|
|
||
|
|
func snakeCase(s string) string {
|
||
|
|
if s == "" {
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
|
||
|
|
var b strings.Builder
|
||
|
|
var prevUpper bool
|
||
|
|
|
||
|
|
for i, c := range s {
|
||
|
|
isUpper := c >= 'A' && c <= 'Z'
|
||
|
|
|
||
|
|
if i > 0 && isUpper {
|
||
|
|
nextLower := false
|
||
|
|
if i+1 < len(s) {
|
||
|
|
nextChar := rune(s[i+1])
|
||
|
|
nextLower = nextChar >= 'a' && nextChar <= 'z'
|
||
|
|
}
|
||
|
|
|
||
|
|
if !prevUpper || nextLower {
|
||
|
|
b.WriteByte('_')
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
b.WriteRune(c)
|
||
|
|
prevUpper = isUpper
|
||
|
|
}
|
||
|
|
|
||
|
|
return strings.ToLower(b.String())
|
||
|
|
}
|