fix: package name

This commit is contained in:
2026-03-23 09:47:14 +03:00
parent 6c307ffaa1
commit 5b5d160cf2
10 changed files with 23 additions and 83 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ Built for game networking where every byte and allocation matters.
## Installation ## Installation
```bash ```bash
go install edmand46/arpack/cmd/arpack@latest go install github.com/edmand46/arpack/cmd/arpack@latest
``` ```
## Usage ## Usage
+2 -2
View File
@@ -1,8 +1,8 @@
package main package main
import ( import (
"edmand46/arpack/generator" "github.com/edmand46/arpack/generator"
"edmand46/arpack/parser" "github.com/edmand46/arpack/parser"
"flag" "flag"
"fmt" "fmt"
"log" "log"
+2 -2
View File
@@ -2,8 +2,8 @@ package e2e
import ( import (
"bytes" "bytes"
"edmand46/arpack/generator" "github.com/edmand46/arpack/generator"
"edmand46/arpack/parser" "github.com/edmand46/arpack/parser"
"fmt" "fmt"
"math" "math"
"os" "os"
+1 -16
View File
@@ -1,18 +1,15 @@
package generator package generator
import ( import (
"edmand46/arpack/parser" "github.com/edmand46/arpack/parser"
"fmt" "fmt"
"strings" "strings"
) )
// GenerateCSharp генерирует C# unsafe код для списка сообщений.
// namespace — пространство имён (например, "Ragono.Messages").
func GenerateCSharp(messages []parser.Message, namespace string) ([]byte, error) { func GenerateCSharp(messages []parser.Message, namespace string) ([]byte, error) {
return GenerateCSharpSchema(parser.Schema{Messages: messages}, namespace) return GenerateCSharpSchema(parser.Schema{Messages: messages}, namespace)
} }
// GenerateCSharpSchema генерирует C# код для полной схемы, включая enum-ы.
func GenerateCSharpSchema(schema parser.Schema, namespace string) ([]byte, error) { func GenerateCSharpSchema(schema parser.Schema, namespace string) ([]byte, error) {
messages := schema.Messages messages := schema.Messages
var b strings.Builder var b strings.Builder
@@ -34,11 +31,9 @@ func GenerateCSharpSchema(schema parser.Schema, namespace string) ([]byte, error
enumNames[enum.Name] = struct{}{} enumNames[enum.Name] = struct{}{}
} }
wroteSection := false
for _, enum := range schema.Enums { for _, enum := range schema.Enums {
writeCSharpEnum(&b, enum) writeCSharpEnum(&b, enum)
b.WriteString("\n") b.WriteString("\n")
wroteSection = true
} }
for i, msg := range messages { for i, msg := range messages {
@@ -47,8 +42,6 @@ func GenerateCSharpSchema(schema parser.Schema, namespace string) ([]byte, error
} }
if i < len(messages)-1 { if i < len(messages)-1 {
b.WriteString("\n") b.WriteString("\n")
} else if wroteSection {
// leave a single blank line between the last enum and the first struct only
} }
} }
@@ -75,13 +68,11 @@ func writeCSharpMessage(b *strings.Builder, msg parser.Message, enumNames map[st
b.WriteString(msg.Name) b.WriteString(msg.Name)
b.WriteString("\n {\n") b.WriteString("\n {\n")
// Поля
for _, f := range msg.Fields { for _, f := range msg.Fields {
fmt.Fprintf(b, " public %s %s;\n", csharpTypeName(f, enumNames), f.Name) fmt.Fprintf(b, " public %s %s;\n", csharpTypeName(f, enumNames), f.Name)
} }
b.WriteString("\n") b.WriteString("\n")
// Serialize
b.WriteString(" public int Serialize(byte* buffer)\n") b.WriteString(" public int Serialize(byte* buffer)\n")
b.WriteString(" {\n") b.WriteString(" {\n")
b.WriteString(" byte* ptr = buffer;\n") b.WriteString(" byte* ptr = buffer;\n")
@@ -97,7 +88,6 @@ func writeCSharpMessage(b *strings.Builder, msg parser.Message, enumNames map[st
b.WriteString(" return (int)(ptr - buffer);\n") b.WriteString(" return (int)(ptr - buffer);\n")
b.WriteString(" }\n\n") b.WriteString(" }\n\n")
// Deserialize
fmt.Fprintf(b, " public static int Deserialize(byte* buffer, out %s msg)\n", msg.Name) fmt.Fprintf(b, " public static int Deserialize(byte* buffer, out %s msg)\n", msg.Name)
b.WriteString(" {\n") b.WriteString(" {\n")
b.WriteString(" byte* ptr = buffer;\n") b.WriteString(" byte* ptr = buffer;\n")
@@ -135,8 +125,6 @@ func writeCSharpBoolGroupDeserialize(b *strings.Builder, recv string, bools []pa
} }
} }
// --- Serialize ---
func writeCSharpSerializeField(b *strings.Builder, f parser.Field, indent string, enumNames map[string]struct{}) error { func writeCSharpSerializeField(b *strings.Builder, f parser.Field, indent string, enumNames map[string]struct{}) error {
switch f.Kind { switch f.Kind {
case parser.KindPrimitive: case parser.KindPrimitive:
@@ -220,7 +208,6 @@ func writeCSharpSerializePrimitive(
case parser.KindUint64: case parser.KindUint64:
fmt.Fprintf(b, "%s*(ulong*)ptr = %s; ptr += 8;\n", indent, valueExpr) fmt.Fprintf(b, "%s*(ulong*)ptr = %s; ptr += 8;\n", indent, valueExpr)
case parser.KindString: case parser.KindString:
// UTF-8: uint16 byteCount + bytes
lenVar := "_slen" + sanitizeVarName(access) lenVar := "_slen" + sanitizeVarName(access)
fmt.Fprintf(b, "%sint %s = %s != null ? Encoding.UTF8.GetByteCount(%s) : 0;\n", fmt.Fprintf(b, "%sint %s = %s != null ? Encoding.UTF8.GetByteCount(%s) : 0;\n",
indent, lenVar, valueExpr, valueExpr) indent, lenVar, valueExpr, valueExpr)
@@ -250,8 +237,6 @@ func writeCSharpSerializeQuant(b *strings.Builder, access string, f parser.Field
return nil return nil
} }
// --- Deserialize ---
func writeCSharpDeserializeField( func writeCSharpDeserializeField(
b *strings.Builder, b *strings.Builder,
recv string, recv string,
+1 -1
View File
@@ -1,7 +1,7 @@
package generator package generator
import ( import (
"edmand46/arpack/parser" "github.com/edmand46/arpack/parser"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
+1 -11
View File
@@ -1,19 +1,16 @@
package generator package generator
import ( import (
"edmand46/arpack/parser" "github.com/edmand46/arpack/parser"
"fmt" "fmt"
"go/format" "go/format"
"strings" "strings"
) )
// GenerateGo генерирует Go-код сериализации для списка сообщений.
// pkgName — имя пакета в котором будет сгенерированный файл.
func GenerateGo(messages []parser.Message, pkgName string) ([]byte, error) { func GenerateGo(messages []parser.Message, pkgName string) ([]byte, error) {
return GenerateGoSchema(parser.Schema{Messages: messages}, pkgName) return GenerateGoSchema(parser.Schema{Messages: messages}, pkgName)
} }
// GenerateGoSchema генерирует Go-код сериализации для полной схемы.
func GenerateGoSchema(schema parser.Schema, pkgName string) ([]byte, error) { func GenerateGoSchema(schema parser.Schema, pkgName string) ([]byte, error) {
messages := schema.Messages messages := schema.Messages
var b strings.Builder var b strings.Builder
@@ -46,7 +43,6 @@ func GenerateGoSchema(schema parser.Schema, pkgName string) ([]byte, error) {
func writeGoMessage(b *strings.Builder, msg parser.Message) error { func writeGoMessage(b *strings.Builder, msg parser.Message) error {
segs := segmentFields(msg.Fields) segs := segmentFields(msg.Fields)
// Marshal
fmt.Fprintf(b, "func (m *%s) Marshal(buf []byte) []byte {\n", msg.Name) fmt.Fprintf(b, "func (m *%s) Marshal(buf []byte) []byte {\n", msg.Name)
for i, seg := range segs { for i, seg := range segs {
if seg.single != nil { if seg.single != nil {
@@ -59,7 +55,6 @@ func writeGoMessage(b *strings.Builder, msg parser.Message) error {
} }
b.WriteString("\treturn buf\n}\n\n") b.WriteString("\treturn buf\n}\n\n")
// Unmarshal — возвращает кол-во потреблённых байт
fmt.Fprintf(b, "func (m *%s) Unmarshal(data []byte) (int, error) {\n", msg.Name) fmt.Fprintf(b, "func (m *%s) Unmarshal(data []byte) (int, error) {\n", msg.Name)
minSize := packedMinWireSize(msg.Fields) minSize := packedMinWireSize(msg.Fields)
fmt.Fprintf(b, "\tif len(data) < %d {\n", minSize) fmt.Fprintf(b, "\tif len(data) < %d {\n", minSize)
@@ -99,8 +94,6 @@ func writeGoBoolGroupUnmarshal(b *strings.Builder, recv string, bools []parser.F
} }
} }
// --- Marshal ---
func writeGoMarshalField(b *strings.Builder, recv string, f parser.Field, indent string) error { func writeGoMarshalField(b *strings.Builder, recv string, f parser.Field, indent string) error {
access := recv + "." + f.Name access := recv + "." + f.Name
switch f.Kind { switch f.Kind {
@@ -197,8 +190,6 @@ func writeGoMarshalQuant(b *strings.Builder, access string, f parser.Field, inde
return nil return nil
} }
// --- Unmarshal ---
func writeGoUnmarshalField(b *strings.Builder, recv string, f parser.Field, indent string) error { func writeGoUnmarshalField(b *strings.Builder, recv string, f parser.Field, indent string) error {
access := recv + "." + f.Name access := recv + "." + f.Name
switch f.Kind { switch f.Kind {
@@ -361,7 +352,6 @@ func goUnmarshalValueExpr(expr string, f parser.Field) string {
return f.NamedType + "(" + expr + ")" return f.NamedType + "(" + expr + ")"
} }
// sanitizeVarName превращает "m.Pos[_i]" в "_mPos_i".
func sanitizeVarName(s string) string { func sanitizeVarName(s string) string {
var b strings.Builder var b strings.Builder
for _, c := range s { for _, c := range s {
+3 -10
View File
@@ -1,20 +1,16 @@
package generator package generator
import "edmand46/arpack/parser" import "github.com/edmand46/arpack/parser"
// segment — либо группа bool (1–8 полей → 1 байт), либо одиночное поле.
type segment struct { type segment struct {
bools []parser.Field // non-empty: bool-группа bools []parser.Field
single *parser.Field // non-nil: любое не-bool поле single *parser.Field
} }
// isBoolField возвращает true если поле — нативный bool (не массив, не слайс).
func isBoolField(f parser.Field) bool { func isBoolField(f parser.Field) bool {
return f.Kind == parser.KindPrimitive && f.Primitive == parser.KindBool return f.Kind == parser.KindPrimitive && f.Primitive == parser.KindBool
} }
// segmentFields разбивает поля структуры на сегменты.
// Последовательные bool-поля группируются по 8 в один сегмент.
func segmentFields(fields []parser.Field) []segment { func segmentFields(fields []parser.Field) []segment {
var segs []segment var segs []segment
i := 0 i := 0
@@ -25,7 +21,6 @@ func segmentFields(fields []parser.Field) []segment {
i++ i++
continue continue
} }
// Собираем последовательные bool-поля группами по 8
for i < len(fields) && isBoolField(fields[i]) { for i < len(fields) && isBoolField(fields[i]) {
var group []parser.Field var group []parser.Field
for i < len(fields) && isBoolField(fields[i]) && len(group) < 8 { for i < len(fields) && isBoolField(fields[i]) && len(group) < 8 {
@@ -38,7 +33,6 @@ func segmentFields(fields []parser.Field) []segment {
return segs return segs
} }
// packedMinWireSize вычисляет минимальный размер буфера с учётом упаковки bool.
func packedMinWireSize(fields []parser.Field) int { func packedMinWireSize(fields []parser.Field) int {
total := 0 total := 0
for _, seg := range segmentFields(fields) { for _, seg := range segmentFields(fields) {
@@ -50,7 +44,6 @@ func packedMinWireSize(fields []parser.Field) int {
total += s total += s
} }
} else { } else {
// Группа bool → 1 байт
total += 1 total += 1
} }
} }
+1 -1
View File
@@ -1 +1 @@
module edmand46/arpack module github.com/edmand46/arpack
+1 -5
View File
@@ -12,7 +12,6 @@ import (
"strings" "strings"
) )
// ParseFile парсит Go-файл и возвращает список сообщений.
func ParseFile(path string) ([]Message, error) { func ParseFile(path string) ([]Message, error) {
schema, err := ParseSchemaFile(path) schema, err := ParseSchemaFile(path)
if err != nil { if err != nil {
@@ -21,7 +20,6 @@ func ParseFile(path string) ([]Message, error) {
return schema.Messages, nil return schema.Messages, nil
} }
// ParseSource парсит исходный код из строки (удобно для тестов).
func ParseSource(src string) ([]Message, error) { func ParseSource(src string) ([]Message, error) {
schema, err := ParseSchemaSource(src) schema, err := ParseSchemaSource(src)
if err != nil { if err != nil {
@@ -30,7 +28,6 @@ func ParseSource(src string) ([]Message, error) {
return schema.Messages, nil return schema.Messages, nil
} }
// ParseSchemaFile парсит файл и возвращает полную схему: сообщения и enum-ы.
func ParseSchemaFile(path string) (Schema, error) { func ParseSchemaFile(path string) (Schema, error) {
fset := token.NewFileSet() fset := token.NewFileSet()
@@ -42,7 +39,6 @@ func ParseSchemaFile(path string) (Schema, error) {
return parseASTFile(fset, f) return parseASTFile(fset, f)
} }
// ParseSchemaSource парсит исходник и возвращает полную схему.
func ParseSchemaSource(src string) (Schema, error) { func ParseSchemaSource(src string) (Schema, error) {
fset := token.NewFileSet() fset := token.NewFileSet()
@@ -200,7 +196,7 @@ func parseStruct(
for _, astField := range st.Fields.List { for _, astField := range st.Fields.List {
if len(astField.Names) == 0 { if len(astField.Names) == 0 {
continue // embedded field, пропускаем continue
} }
var rawTag string var rawTag string
+10 -34
View File
@@ -1,6 +1,5 @@
package parser package parser
// PrimitiveKind — конкретный примитивный тип.
type PrimitiveKind int type PrimitiveKind int
const ( const (
@@ -18,24 +17,21 @@ const (
KindString KindString
) )
// FieldKind — категория поля.
type FieldKind int type FieldKind int
const ( const (
KindPrimitive FieldKind = iota // float, int, uint, bool, string KindPrimitive FieldKind = iota
KindNested // ссылка на другой Message KindNested
KindFixedArray // [N]T KindFixedArray
KindSlice // []T KindSlice
) )
// QuantInfo описывает квантизацию float → uint8/uint16.
type QuantInfo struct { type QuantInfo struct {
Min float64 Min float64
Max float64 Max float64
Bits int // 8 или 16, default 16 Bits int // 8 or 16, default 16
} }
// MaxUint — максимальное целое значение для данного числа бит.
func (q *QuantInfo) MaxUint() float64 { func (q *QuantInfo) MaxUint() float64 {
if q.Bits == 8 { if q.Bits == 8 {
return 255 return 255
@@ -43,31 +39,24 @@ func (q *QuantInfo) MaxUint() float64 {
return 65535 return 65535
} }
// WireBytes — размер на проводе в байтах.
func (q *QuantInfo) WireBytes() int { func (q *QuantInfo) WireBytes() int {
return q.Bits / 8 return q.Bits / 8
} }
// Field — одно поле структуры-сообщения.
type Field struct { type Field struct {
Name string Name string
Kind FieldKind Kind FieldKind
// KindPrimitive
Primitive PrimitiveKind Primitive PrimitiveKind
NamedType string NamedType string
Quant *QuantInfo // nil если нет квантизации Quant *QuantInfo
// KindNested
TypeName string TypeName string
// KindFixedArray / KindSlice
Elem *Field Elem *Field
FixedLen int // >0 только для KindFixedArray FixedLen int
} }
// WireSize — размер в байтах на проводе.
// Возвращает -1 для полей переменного размера.
func (f *Field) WireSize() int { func (f *Field) WireSize() int {
switch f.Kind { switch f.Kind {
case KindPrimitive: case KindPrimitive:
@@ -76,7 +65,7 @@ func (f *Field) WireSize() int {
} }
return primitiveWireSize(f.Primitive) return primitiveWireSize(f.Primitive)
case KindNested: case KindNested:
return -1 // зависит от конкретного типа, узнаём через Message.MinWireSize return -1
case KindFixedArray: case KindFixedArray:
elemSize := f.Elem.WireSize() elemSize := f.Elem.WireSize()
if elemSize == -1 { if elemSize == -1 {
@@ -84,7 +73,7 @@ func (f *Field) WireSize() int {
} }
return f.FixedLen * elemSize return f.FixedLen * elemSize
case KindSlice: case KindSlice:
return -1 // uint16 len + переменная часть return -1
} }
return 0 return 0
} }
@@ -105,7 +94,6 @@ func primitiveWireSize(k PrimitiveKind) int {
return 0 return 0
} }
// IsIntegralPrimitive — подходит ли тип как базовый для enum.
func IsIntegralPrimitive(k PrimitiveKind) bool { func IsIntegralPrimitive(k PrimitiveKind) bool {
switch k { switch k {
case KindInt8, KindInt16, KindInt32, KindInt64, KindUint8, KindUint16, KindUint32, KindUint64: case KindInt8, KindInt16, KindInt32, KindInt64, KindUint8, KindUint16, KindUint32, KindUint64:
@@ -114,7 +102,6 @@ func IsIntegralPrimitive(k PrimitiveKind) bool {
return false return false
} }
// GoTypeName — имя типа в Go.
func (f *Field) GoTypeName() string { func (f *Field) GoTypeName() string {
switch f.Kind { switch f.Kind {
case KindPrimitive: case KindPrimitive:
@@ -132,7 +119,6 @@ func (f *Field) GoTypeName() string {
return "unknown" return "unknown"
} }
// CSharpTypeName — имя типа в C#.
func (f *Field) CSharpTypeName() string { func (f *Field) CSharpTypeName() string {
switch f.Kind { switch f.Kind {
case KindPrimitive: case KindPrimitive:
@@ -180,7 +166,6 @@ func primitiveGoName(k PrimitiveKind) string {
return "unknown" return "unknown"
} }
// GoPrimitiveTypeName — базовый примитивный тип поля в Go.
func (f *Field) GoPrimitiveTypeName() string { func (f *Field) GoPrimitiveTypeName() string {
return primitiveGoName(f.Primitive) return primitiveGoName(f.Primitive)
} }
@@ -215,7 +200,6 @@ func primitiveCSharpName(k PrimitiveKind) string {
return "unknown" return "unknown"
} }
// CSharpPrimitiveTypeName — базовый примитивный тип поля в C#.
func (f *Field) CSharpPrimitiveTypeName() string { func (f *Field) CSharpPrimitiveTypeName() string {
return primitiveCSharpName(f.Primitive) return primitiveCSharpName(f.Primitive)
} }
@@ -234,42 +218,35 @@ func itoa(n int) string {
return string(buf[pos:]) return string(buf[pos:])
} }
// Message — описание одной структуры-сообщения.
type Message struct { type Message struct {
PackageName string PackageName string
Name string Name string
Fields []Field Fields []Field
} }
// EnumValue — одно именованное значение enum.
type EnumValue struct { type EnumValue struct {
Name string Name string
Value string Value string
} }
// Enum — enum-подобный тип на основе именованного примитива.
type Enum struct { type Enum struct {
Name string Name string
Primitive PrimitiveKind Primitive PrimitiveKind
Values []EnumValue Values []EnumValue
} }
// Schema — полная модель входного файла.
type Schema struct { type Schema struct {
PackageName string PackageName string
Messages []Message Messages []Message
Enums []Enum Enums []Enum
} }
// MinWireSize — минимальный гарантированный размер в байтах.
// Для вложенных типов считается только если размер известен заранее.
// Строки и слайсы считаются как 2 байта (length prefix).
func (m *Message) MinWireSize() int { func (m *Message) MinWireSize() int {
total := 0 total := 0
for _, f := range m.Fields { for _, f := range m.Fields {
s := f.WireSize() s := f.WireSize()
if s == -1 { if s == -1 {
total += 2 // минимум: length prefix total += 2
} else { } else {
total += s total += s
} }
@@ -277,7 +254,6 @@ func (m *Message) MinWireSize() int {
return total return total
} }
// HasVariableFields — есть ли поля переменного размера.
func (m *Message) HasVariableFields() bool { func (m *Message) HasVariableFields() bool {
for _, f := range m.Fields { for _, f := range m.Fields {
if f.WireSize() == -1 { if f.WireSize() == -1 {