diff --git a/ent/array.go b/ent/array.go index 537d328..9e332cb 100644 --- a/ent/array.go +++ b/ent/array.go @@ -20,7 +20,7 @@ func arrayType(spec *typeSpec, env *Env) tÿpe { elemSpec := *spec elemSpec.typeName = elemName elemType := parseTypeSpec(&elemSpec, env) - return array_t{elemType, count} + return &array_t{elemType, count} } func parseArrayName(s string) (string, int) { @@ -49,18 +49,28 @@ type array_t struct { count int } +func (t *array_t) nü() value { return array{t: t, slots: make([]value, t.count)} } func (t array_t) typeName() string { return fmt.Sprintf("array:%s", t.elem.typeName()) } -func (t array_t) read(r bit.Reader) (value, error) { - var err error - v := make(array, t.count) - for i := range v { - v[i], err = t.elem.read(r) - if err != nil { - return nil, wrap(err, "array read error at index %d", i) +type array struct { + t *array_t + slots []value +} + +func (a array) tÿpe() tÿpe { return a.t } + +func (a array) read(r bit.Reader) error { + for i := range a.slots { + if a.slots[i] == nil { + a.slots[i] = a.t.elem.nü() + } + if err := a.slots[i].read(r); err != nil { + return wrap(err, "array read error at index %d", i) } } - return v, r.Err() + return r.Err() } -type array []value +func (a array) String() string { + return fmt.Sprintf("%s%v", a.t.typeName(), a.slots) +} diff --git a/ent/atoms.go b/ent/atoms.go index 2d2a488..2eaf83f 100644 --- a/ent/atoms.go +++ b/ent/atoms.go @@ -1,75 +1,254 @@ package ent import ( + "fmt" "github.com/jordanorelli/hyperstone/bit" + "strconv" ) -var atom_types = []typeLiteral{ - { - "bool", - func(r bit.Reader) (value, error) { - return bit.ReadBool(r), r.Err() - }, +// ------------------------------------------------------------------------------ +// bool +// ------------------------------------------------------------------------------ + +var bool_t = &typeLiteral{ + name: "bool", + newFn: func() value { + return new(bool_v) }, - { - "uint8", - func(r bit.Reader) (value, error) { - // TODO: bounds check here - return uint8(bit.ReadVarInt(r)), r.Err() - }, +} + +type bool_v bool + +func (v bool_v) tÿpe() tÿpe { return bool_t } + +func (v *bool_v) read(r bit.Reader) error { + *v = bool_v(bit.ReadBool(r)) + return r.Err() +} + +func (v bool_v) String() string { + if v { + return "true" + } + return "false" +} + +// ------------------------------------------------------------------------------ +// uint8 +// ------------------------------------------------------------------------------ + +var uint8_t = &typeLiteral{ + name: "uint8", + newFn: func() value { + return new(uint8_v) }, - { - "uint16", - func(r bit.Reader) (value, error) { - // TODO: bounds check here - return uint16(bit.ReadVarInt(r)), r.Err() - }, +} + +type uint8_v uint8 + +func (v uint8_v) tÿpe() tÿpe { return uint8_t } +func (v *uint8_v) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + if u > 1<<8-1 { + return fmt.Errorf("uint8 overflow: %d", u) + } + *v = uint8_v(u) + return r.Err() +} + +func (v uint8_v) String() string { + return strconv.FormatUint(uint64(v), 10) +} + +// ------------------------------------------------------------------------------ +// uint16 +// ------------------------------------------------------------------------------ + +var uint16_t = &typeLiteral{ + name: "uint16", + newFn: func() value { + return new(uint16_v) }, - { - "uint32", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt32(r), r.Err() - }, +} + +type uint16_v uint16 + +func (v uint16_v) tÿpe() tÿpe { return uint16_t } +func (v *uint16_v) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + if u > 1<<16-1 { + return fmt.Errorf("uint16 overflow: %d", u) + } + *v = uint16_v(u) + return r.Err() +} + +func (v uint16_v) String() string { + return strconv.FormatUint(uint64(v), 10) +} + +// ------------------------------------------------------------------------------ +// uint32 +// ------------------------------------------------------------------------------ + +var uint32_t = &typeLiteral{ + name: "uint32", + newFn: func() value { + return new(uint32_v) }, - { - "uint64", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }, +} + +type uint32_v uint32 + +func (v uint32_v) tÿpe() tÿpe { return uint32_t } +func (v *uint32_v) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + if u > 1<<32-1 { + return fmt.Errorf("uint32 overflow: %d", u) + } + *v = uint32_v(u) + return r.Err() +} + +func (v uint32_v) String() string { + return strconv.FormatUint(uint64(v), 0) +} + +// ------------------------------------------------------------------------------ +// uint64 +// ------------------------------------------------------------------------------ + +var uint64_t = &typeLiteral{ + name: "uint64", + newFn: func() value { + return new(uint64_v) }, - { - "int8", - func(r bit.Reader) (value, error) { - // TODO: bounds check here - return int8(bit.ReadZigZag32(r)), r.Err() - }, +} + +type uint64_v uint64 + +func (v uint64_v) tÿpe() tÿpe { return uint64_t } +func (v *uint64_v) read(r bit.Reader) error { + *v = uint64_v(bit.ReadVarInt(r)) + return r.Err() +} + +func (v uint64_v) String() string { + return strconv.FormatUint(uint64(v), 10) +} + +// ------------------------------------------------------------------------------ +// int8 +// ------------------------------------------------------------------------------ + +var int8_t = &typeLiteral{ + name: "int8", + newFn: func() value { + return new(int8_v) }, - { - "int32", - func(r bit.Reader) (value, error) { - return bit.ReadZigZag32(r), r.Err() - }, +} + +type int8_v int8 + +func (v int8_v) tÿpe() tÿpe { return int8_t } +func (v *int8_v) read(r bit.Reader) error { + // TODO: bounds check here? + *v = int8_v(bit.ReadZigZag32(r)) + return r.Err() +} + +func (v int8_v) String() string { + return strconv.FormatInt(int64(v), 10) +} + +// ------------------------------------------------------------------------------ +// int32 +// ------------------------------------------------------------------------------ + +var int32_t = &typeLiteral{ + name: "int32", + newFn: func() value { + return new(int32_v) }, - { - "CUtlStringToken", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }, +} + +type int32_v int32 + +func (v int32_v) tÿpe() tÿpe { return int32_t } +func (v *int32_v) read(r bit.Reader) error { + *v = int32_v(bit.ReadZigZag32(r)) + return r.Err() +} + +func (v int32_v) String() string { + return strconv.FormatInt(int64(v), 10) +} + +// ------------------------------------------------------------------------------ +// CUtlStringToken +// +// weirdly, this type isn't a string; it's actually a number. The number +// presumably indicates some value on a symbol table. +// ------------------------------------------------------------------------------ + +var stringToken_t = &typeLiteral{ + name: "CUtlStringToken", + newFn: func() value { + return new(stringToken_v) }, - { - "Color", - func(r bit.Reader) (value, error) { - u := bit.ReadVarInt(r) - return color{ - r: uint8(u >> 6 & 0xff), - g: uint8(u >> 4 & 0xff), - b: uint8(u >> 2 & 0xff), - a: uint8(u >> 0 & 0xff), - }, r.Err() - }, +} + +type stringToken_v uint64 + +func (v stringToken_v) tÿpe() tÿpe { return stringToken_t } +func (v *stringToken_v) read(r bit.Reader) error { + *v = stringToken_v(bit.ReadVarInt(r)) + return r.Err() +} + +func (v stringToken_v) String() string { + return fmt.Sprintf("token:%d", v) +} + +// ------------------------------------------------------------------------------ +// Color +// ------------------------------------------------------------------------------ + +var color_t = &typeLiteral{ + name: "Color", + newFn: func() value { + return new(color) }, } +type color struct{ r, g, b, a uint8 } + +func (c color) tÿpe() tÿpe { return color_t } +func (c *color) read(r bit.Reader) error { + u := bit.ReadVarInt(r) + c.r = uint8(u >> 6 & 0xff) + c.g = uint8(u >> 4 & 0xff) + c.b = uint8(u >> 2 & 0xff) + c.a = uint8(u >> 0 & 0xff) + return r.Err() +} + +func (c color) String() string { + return fmt.Sprintf("#%x%x%x%x", c.r, c.g, c.b, c.a) +} + +var atom_types = []tÿpe{ + bool_t, + uint8_t, + uint16_t, + uint32_t, + uint64_t, + int8_t, + int32_t, + stringToken_t, + color_t, +} + func atomType(spec *typeSpec, env *Env) tÿpe { for _, t := range atom_types { if t.typeName() == spec.typeName { @@ -79,5 +258,3 @@ func atomType(spec *typeSpec, env *Env) tÿpe { } return nil } - -type color struct{ r, g, b, a uint8 } diff --git a/ent/class.go b/ent/class.go index 8aa1ca7..6ac14fc 100644 --- a/ent/class.go +++ b/ent/class.go @@ -24,8 +24,8 @@ func (c *class) read(r bit.Reader) (value, error) { return c.nü(), nil } -func (c *class) nü() entity { - return entity{class: c, slots: make([]value, len(c.fields))} +func (c *class) nü() value { + return &entity{class: c, slots: make([]value, len(c.fields))} } type classHistory struct { diff --git a/ent/entity.go b/ent/entity.go index d4fdecb..d93ea20 100644 --- a/ent/entity.go +++ b/ent/entity.go @@ -1,6 +1,7 @@ package ent import ( + "fmt" "github.com/jordanorelli/hyperstone/bit" ) @@ -31,6 +32,12 @@ func (e *entity) className() string { return "" } +func (e *entity) String() string { + return fmt.Sprintf("%s{%v}", e.class.typeName(), e.slots) +} + +func (e *entity) tÿpe() tÿpe { return e.class } func (e *entity) slotType(i int) tÿpe { return e.class.fields[i].tÿpe } +func (e *entity) slotName(i int) string { return e.class.fields[i].name } func (e *entity) setSlotValue(i int, v value) { e.slots[i] = v } func (e *entity) getSlotValue(i int) value { return e.slots[i] } diff --git a/ent/float.go b/ent/float.go index d5dea25..6af3cd6 100644 --- a/ent/float.go +++ b/ent/float.go @@ -2,6 +2,7 @@ package ent import ( "math" + "strconv" "github.com/jordanorelli/hyperstone/bit" ) @@ -23,12 +24,7 @@ func floatType(spec *typeSpec, env *Env) tÿpe { } if spec.encoder == "coord" { Debug.Printf(" coord float type") - return typeLiteral{ - "float:coord", - func(r bit.Reader) (value, error) { - return bit.ReadCoord(r), r.Err() - }, - } + return coord_t } if spec.serializer == "simulationtime" { return nil @@ -36,18 +32,50 @@ func floatType(spec *typeSpec, env *Env) tÿpe { switch spec.bits { case 0, 32: Debug.Printf(" std float type") - return typeLiteral{ - "float:std", - func(r bit.Reader) (value, error) { - // TODO: check uint32 overflow here? - return math.Float32frombits(uint32(r.ReadBits(32))), r.Err() - }, - } + return float_t default: return qFloatType(spec, env) } } +var coord_t = &typeLiteral{ + name: "coord", + newFn: func() value { + return new(coord_v) + }, +} + +type coord_v float32 + +func (v coord_v) tÿpe() tÿpe { return coord_t } +func (v *coord_v) read(r bit.Reader) error { + *v = coord_v(bit.ReadCoord(r)) + return r.Err() +} + +func (v coord_v) String() string { + return strconv.FormatFloat(float64(v), 'f', 3, 32) +} + +var float_t = &typeLiteral{ + name: "float", + newFn: func() value { + return new(float_v) + }, +} + +type float_v float32 + +func (v float_v) tÿpe() tÿpe { return float_t } +func (v *float_v) read(r bit.Reader) error { + *v = float_v(math.Float32frombits(uint32(r.ReadBits(32)))) + return r.Err() +} + +func (v float_v) String() string { + return strconv.FormatFloat(float64(v), 'f', 3, 32) +} + func qFloatType(spec *typeSpec, env *Env) tÿpe { if spec.bits < 0 { return typeError("quantized float has invalid negative bit count specifier") @@ -56,7 +84,7 @@ func qFloatType(spec *typeSpec, env *Env) tÿpe { return typeError("quantized float has invalid negative range") } - t := qfloat_t{typeSpec: *spec} + t := &qfloat_t{typeSpec: *spec} t.span = t.high - t.low t.intervals = uint(1<", v.t.typeName(), v.slots) } diff --git a/ent/handle.go b/ent/handle.go index 56110ab..3ea0f10 100644 --- a/ent/handle.go +++ b/ent/handle.go @@ -1,12 +1,32 @@ package ent import ( + "fmt" + "github.com/jordanorelli/hyperstone/bit" ) +type handle_t string + +func (t handle_t) typeName() string { return string(t) } +func (t *handle_t) nü() value { return &handle{t: t} } + // a handle represents a soft pointer to an entity. handles are represented by // IDs and can cross the client-server divide. -type handle int +type handle struct { + t tÿpe + id uint64 +} + +func (h handle) tÿpe() tÿpe { return h.t } +func (h *handle) read(r bit.Reader) error { + h.id = bit.ReadVarInt(r) + return r.Err() +} + +func (h handle) String() string { + return fmt.Sprintf("handle<%s>: %d", h.t.typeName(), h.id) +} func handleType(spec *typeSpec, env *Env) tÿpe { if spec.typeName != "CGameSceneNodeHandle" { @@ -14,10 +34,6 @@ func handleType(spec *typeSpec, env *Env) tÿpe { } Debug.Printf(" handle type") - return typeLiteral{ - "handle:CGameSceneNodeHandle", - func(r bit.Reader) (value, error) { - return handle(bit.ReadVarInt(r)), r.Err() - }, - } + t := handle_t(spec.typeName) + return &t } diff --git a/ent/hseq.go b/ent/hseq.go index d4ae875..e9d3760 100644 --- a/ent/hseq.go +++ b/ent/hseq.go @@ -1,19 +1,34 @@ package ent import ( + "fmt" + "github.com/jordanorelli/hyperstone/bit" ) +var hseq_t = &typeLiteral{ + name: "HSequence", + newFn: func() value { + return new(hseq_v) + }, +} + +type hseq_v uint64 + +func (v hseq_v) tÿpe() tÿpe { return hseq_t } +func (v *hseq_v) read(r bit.Reader) error { + *v = hseq_v(bit.ReadVarInt(r) - 1) + return r.Err() +} + +func (v hseq_v) String() string { + return fmt.Sprintf("hseq:%d", uint64(v)) +} + func hSeqType(spec *typeSpec, env *Env) tÿpe { if spec.typeName != "HSequence" { return nil } - Debug.Printf(" hsequence type") - return typeLiteral{ - "HSequence", - func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r) - 1, r.Err() - }, - } + return hseq_t } diff --git a/ent/qangle.go b/ent/qangle.go index 0134d30..a960c9f 100644 --- a/ent/qangle.go +++ b/ent/qangle.go @@ -1,12 +1,40 @@ package ent import ( - "github.com/jordanorelli/hyperstone/bit" + "fmt" "math" + + "github.com/jordanorelli/hyperstone/bit" ) +var qangle_t = &typeLiteral{ + name: "qangle", + newFn: func() value { + return new(qangle) + }, +} + type qangle struct{ pitch, yaw, roll float32 } +func (q qangle) tÿpe() tÿpe { return qangle_t } +func (q *qangle) read(r bit.Reader) error { + pitch, yaw, roll := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) + if pitch { + q.pitch = bit.ReadCoord(r) + } + if yaw { + q.yaw = bit.ReadCoord(r) + } + if roll { + q.roll = bit.ReadCoord(r) + } + return r.Err() +} + +func (q qangle) String() string { + return fmt.Sprintf("qangle{%f %f %f}", q.pitch, q.yaw, q.roll) +} + func qAngleType(spec *typeSpec, env *Env) tÿpe { if spec.typeName != "QAngle" { return nil @@ -16,31 +44,16 @@ func qAngleType(spec *typeSpec, env *Env) tÿpe { case spec.bits <= 0 || spec.bits > 32: return typeError("qangle pitch_yaw has invalid bit size: %d", spec.bits) case spec.bits == 32: - return typeLiteral{"qangle:pitchYaw", pitchYaw_t} + return pitchYaw_t default: - return pitchYawAngles_t(spec.bits) + t := pitchYawAngles_t(spec.bits) + return &t } } switch spec.bits { case 0: Debug.Printf(" qangle type") - return typeLiteral{ - "qangle", - func(r bit.Reader) (value, error) { - var q qangle - pitch, yaw, roll := bit.ReadBool(r), bit.ReadBool(r), bit.ReadBool(r) - if pitch { - q.pitch = bit.ReadCoord(r) - } - if yaw { - q.yaw = bit.ReadCoord(r) - } - if roll { - q.roll = bit.ReadCoord(r) - } - return q, nil - }, - } + return qangle_t case 32: return nil default: @@ -48,20 +61,45 @@ func qAngleType(spec *typeSpec, env *Env) tÿpe { } } -func pitchYaw_t(r bit.Reader) (value, error) { - var q qangle - q.pitch = math.Float32frombits(uint32(r.ReadBits(32))) - q.yaw = math.Float32frombits(uint32(r.ReadBits(32))) - return q, r.Err() +var pitchYaw_t = &typeLiteral{ + name: "qangle:pitchYaw", + newFn: func() value { + return new(pitchYaw_v) + }, +} + +type pitchYaw_v qangle + +func (v pitchYaw_v) tÿpe() tÿpe { return pitchYaw_t } +func (v *pitchYaw_v) read(r bit.Reader) error { + v.pitch = math.Float32frombits(uint32(r.ReadBits(32))) + v.yaw = math.Float32frombits(uint32(r.ReadBits(32))) + return r.Err() +} + +func (v pitchYaw_v) String() string { + return fmt.Sprintf("qangle:pitchYaw{%f %f}", v.pitch, v.yaw) } type pitchYawAngles_t uint func (t pitchYawAngles_t) typeName() string { return "qangle:pitchYawAngles" } +func (t *pitchYawAngles_t) nü() value { + return &pitchYawAngles_v{t: t} +} + +type pitchYawAngles_v struct { + t *pitchYawAngles_t + qangle +} + +func (v pitchYawAngles_v) tÿpe() tÿpe { return v.t } +func (v *pitchYawAngles_v) read(r bit.Reader) error { + v.pitch = bit.ReadAngle(r, uint(*v.t)) + v.yaw = bit.ReadAngle(r, uint(*v.t)) + return r.Err() +} -func (t pitchYawAngles_t) read(r bit.Reader) (value, error) { - var q qangle - q.pitch = bit.ReadAngle(r, uint(t)) - q.yaw = bit.ReadAngle(r, uint(t)) - return q, r.Err() +func (v pitchYawAngles_v) String() string { + return fmt.Sprintf("qangle:pitchYawAngles{%f %f}", v.pitch, v.yaw) } diff --git a/ent/selection.go b/ent/selection.go index 12fd643..7f77eb6 100644 --- a/ent/selection.go +++ b/ent/selection.go @@ -20,31 +20,35 @@ func (s selection) String() string { return fmt.Sprint(s.path()) } func (s selection) path() []int { return s.vals[:s.count] } func (s selection) fillSlots(v slotted, r bit.Reader) error { - Debug.Printf("%v fill slots into %v", s, v) - return s.fillSlotsIter(0, v, r) + return s.fillSlotsIter(0, v, v.tÿpe().typeName(), r) } -func (s selection) fillSlotsIter(offset int, dest slotted, r bit.Reader) error { +func (s selection) fillSlotsIter(offset int, dest slotted, path string, r bit.Reader) error { slot := s.vals[offset] if s.count-offset <= 0 { return fmt.Errorf("unable to fill selection %v having count %d at offset %d", s, s.count, offset) } switch s.count - offset { case 1: - t := dest.slotType(slot) - v, err := t.read(r) - if err != nil { + v := dest.slotType(slot).nü() + if err := v.read(r); err != nil { return fmt.Errorf("unable to fill selection: %v", err) } + old := dest.getSlotValue(slot) dest.setSlotValue(slot, v) + Debug.Printf("%v %s.%s (%s) %v -> %v", s, path, dest.slotName(slot), dest.slotType(slot).typeName(), old, v) return nil default: v := dest.getSlotValue(slot) + if v == nil { + v = dest.slotType(slot).nü() + dest.setSlotValue(slot, v) + } vs, ok := v.(slotted) if !ok { - return fmt.Errorf("destination is not slotted") + return fmt.Errorf("dest isn't slotted") } - return s.fillSlotsIter(offset+1, vs, r) + return s.fillSlotsIter(offset+1, vs, fmt.Sprintf("%s.%s", path, dest.slotName(slot)), r) } } diff --git a/ent/slotted.go b/ent/slotted.go index db620a3..cb1a088 100644 --- a/ent/slotted.go +++ b/ent/slotted.go @@ -1,7 +1,9 @@ package ent type slotted interface { + value slotType(int) tÿpe + slotName(int) string setSlotValue(int, value) getSlotValue(int) value } diff --git a/ent/type.go b/ent/type.go index 7238016..3060680 100644 --- a/ent/type.go +++ b/ent/type.go @@ -8,17 +8,17 @@ import ( ) type tÿpe interface { - read(bit.Reader) (value, error) + nü() value typeName() string } type typeLiteral struct { - name string - readFn func(r bit.Reader) (value, error) + name string + newFn func() value } -func (t typeLiteral) typeName() string { return t.name } -func (t typeLiteral) read(r bit.Reader) (value, error) { return t.readFn(r) } +func (t typeLiteral) nü() value { return t.newFn() } +func (t typeLiteral) typeName() string { return t.name } type typeParseFn func(*typeSpec, *Env) tÿpe @@ -42,14 +42,32 @@ func parseTypeSpec(spec *typeSpec, env *Env) tÿpe { hSeqType, genericType, vectorType, classType, unknownType) } +type unknown_t string + +func (t unknown_t) typeName() string { return string(t) } +func (t *unknown_t) nü() value { + return &unknown_v{t: t} +} + +type unknown_v struct { + t tÿpe + v uint64 +} + +func (v unknown_v) tÿpe() tÿpe { return v.t } +func (v *unknown_v) read(r bit.Reader) error { + v.v = bit.ReadVarInt(r) + return r.Err() +} + +func (v unknown_v) String() string { + return fmt.Sprintf("%s(unknown):%d", v.t.typeName(), v.v) +} + func unknownType(spec *typeSpec, env *Env) tÿpe { Debug.Printf("Unknown Type: %v", spec) - return typeLiteral{ - name: fmt.Sprintf("unknown:%s", spec.typeName), - readFn: func(r bit.Reader) (value, error) { - return bit.ReadVarInt(r), r.Err() - }, - } + t := unknown_t(spec.typeName) + return &t } // a type error is both an error and a type. It represents a type that we were @@ -62,11 +80,9 @@ func typeError(t string, args ...interface{}) tÿpe { type error_t string +func (e error_t) nü() value { panic("can't create an error val like that") } func (e error_t) typeName() string { return "error" } func (e error_t) Error() string { return string(e) } -func (e error_t) read(r bit.Reader) (value, error) { - return nil, fmt.Errorf("type error: %s", string(e)) -} type typeSpec struct { name string diff --git a/ent/value.go b/ent/value.go index 2b252df..b03be93 100644 --- a/ent/value.go +++ b/ent/value.go @@ -1,3 +1,11 @@ package ent -type value interface{} +import ( + "github.com/jordanorelli/hyperstone/bit" +) + +type value interface { + String() string + tÿpe() tÿpe + read(bit.Reader) error +} diff --git a/ent/vector.go b/ent/vector.go index c8f7e57..37bfc5b 100644 --- a/ent/vector.go +++ b/ent/vector.go @@ -5,8 +5,6 @@ import ( "github.com/jordanorelli/hyperstone/bit" ) -type vector struct{ x, y, z float32 } - func vectorType(spec *typeSpec, env *Env) tÿpe { if spec.encoder != "" { return nil @@ -18,7 +16,7 @@ func vectorType(spec *typeSpec, env *Env) tÿpe { if t == nil { return nil } - return vector_t{elem: t} + return &vector_t{elem: t} } type vector_t struct { @@ -29,19 +27,41 @@ func (t vector_t) typeName() string { return fmt.Sprintf("vector:%s", t.elem.typeName()) } -func (t vector_t) read(r bit.Reader) (value, error) { - var err error - var v interface{} - read := func(f *float32) { - if err != nil { - return +func (t *vector_t) nü() value { + return &vector{t: t} +} + +type vector struct { + t tÿpe + x, y, z value +} + +func (v vector) tÿpe() tÿpe { return v.t } + +func (v *vector) read(r bit.Reader) error { + if v.x == nil { + v.x = v.t.nü() + } + if v.y == nil { + v.y = v.t.nü() + } + if v.z == nil { + v.z = v.t.nü() + } + + type fn func(bit.Reader) error + coalesce := func(fns ...fn) error { + for _, f := range fns { + if err := f(r); err != nil { + return err + } } - v, err = t.elem.read(r) - *f = v.(float32) + return nil } - var out vector - read(&out.x) - read(&out.y) - read(&out.z) - return out, err + + return coalesce(v.x.read, v.y.read, v.z.read) +} + +func (v vector) String() string { + return fmt.Sprintf("vector<%s>{%s %s %s}", v.t.typeName, v.x, v.y, v.z) }