fuller implementation of chunk and unmarshal
This commit is contained in:
		
							parent
							
								
									3004475000
								
							
						
					
					
						commit
						9514f9c374
					
				| 
						 | 
				
			
			@ -7,7 +7,7 @@ func Map[T, V any](c Vector[T], f func(T) V) Vector[V] {
 | 
			
		|||
		out[index] = f(c[index])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return From(out)
 | 
			
		||||
	return From(out...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Filter[T any](v Vector[T], f func(T) bool) Vector[T] {
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ func Take[T any](v Vector[T], howMany int) Vector[T] {
 | 
			
		|||
	if len(v) < howMany {
 | 
			
		||||
		howMany = len(v)
 | 
			
		||||
	}
 | 
			
		||||
	return From(v[:howMany-1])
 | 
			
		||||
	return From(v[:howMany-1]...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Any[T any](v Vector[T], f func(T) bool) bool {
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +39,15 @@ func Any[T any](v Vector[T], f func(T) bool) bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func AnyOf[T comparable](v Vector[T], t T) bool {
 | 
			
		||||
	for _, vT := range v {
 | 
			
		||||
		if vT == t {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type numeric interface {
 | 
			
		||||
	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~float32 | ~float64
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ func WithCap[T any](capacity int) Vector[T] {
 | 
			
		|||
	return make(Vector[T], 0, capacity)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func From[T any](v []T) Vector[T] {
 | 
			
		||||
func From[T any](v ...T) Vector[T] {
 | 
			
		||||
	return Vector[T](v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ func (v Vector[T]) Append(t ...T) Vector[T] {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (v Vector[T]) Remove(index int) Vector[T] {
 | 
			
		||||
	return From(append(v[:index], v[index+1:]...))
 | 
			
		||||
	return From(append(v[:index], v[index+1:]...)...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Faster remove function that can be used on vectors where ordering doesn't matter
 | 
			
		||||
| 
						 | 
				
			
			@ -110,12 +110,12 @@ func (v Vector[T]) Sub(start, end int) Vector[T] {
 | 
			
		|||
		v.panicIndex(end)
 | 
			
		||||
	}
 | 
			
		||||
	if start < 0 {
 | 
			
		||||
		return From(v[:end])
 | 
			
		||||
		return From(v[:end]...)
 | 
			
		||||
	}
 | 
			
		||||
	if end < 0 {
 | 
			
		||||
		return From(v[start:])
 | 
			
		||||
		return From(v[start:]...)
 | 
			
		||||
	}
 | 
			
		||||
	return From(v[start:end])
 | 
			
		||||
	return From(v[start:end]...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v Vector[T]) Filter(f func(T) bool) Vector[T] {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,18 +59,18 @@ func TestVector(t *testing.T) {
 | 
			
		|||
			}
 | 
			
		||||
		},
 | 
			
		||||
		"append": func(that *require.Assertions) {
 | 
			
		||||
			v := coll.From([]int{1, 2, 3}).Append(4, 5, 6)
 | 
			
		||||
			v := coll.From([]int{1, 2, 3}...).Append(4, 5, 6)
 | 
			
		||||
			that.Equal(6, len(v))
 | 
			
		||||
			that.EqualValues([]int{1, 2, 3, 4, 5, 6}, v)
 | 
			
		||||
		},
 | 
			
		||||
		"getsoft": func(that *require.Assertions) {
 | 
			
		||||
			v := coll.From([]int{1, 2, 3})
 | 
			
		||||
			v := coll.From([]int{1, 2, 3}...)
 | 
			
		||||
			that.Equal(2, v.GetSoft(1))
 | 
			
		||||
			that.Zero(v.GetSoft(-1000))
 | 
			
		||||
			that.Zero(v.GetSoft(1000))
 | 
			
		||||
		},
 | 
			
		||||
		"get": func(that *require.Assertions) {
 | 
			
		||||
			v := coll.From([]int{1, 2, 3})
 | 
			
		||||
			v := coll.From([]int{1, 2, 3}...)
 | 
			
		||||
			that.Equal(2, v.Get(1))
 | 
			
		||||
			that.Panics(func() {
 | 
			
		||||
				v.Get(-1000)
 | 
			
		||||
| 
						 | 
				
			
			@ -80,7 +80,7 @@ func TestVector(t *testing.T) {
 | 
			
		|||
			})
 | 
			
		||||
		},
 | 
			
		||||
		"clone": func(that *require.Assertions) {
 | 
			
		||||
			v := coll.From([]int{1, 2, 3})
 | 
			
		||||
			v := coll.From([]int{1, 2, 3}...)
 | 
			
		||||
 | 
			
		||||
			v1 := v.Clone().Set(1, 1)
 | 
			
		||||
			that.Equal(3, len(v1))
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ func TestVector(t *testing.T) {
 | 
			
		|||
			that.EqualValues([]int{1, 2, 3}, v)
 | 
			
		||||
		},
 | 
			
		||||
		"set": func(that *require.Assertions) {
 | 
			
		||||
			v := coll.From([]int{1, 2, 3})
 | 
			
		||||
			v := coll.From([]int{1, 2, 3}...)
 | 
			
		||||
 | 
			
		||||
			v1 := v.Clone().Set(1, 1)
 | 
			
		||||
			that.Equal(3, len(v1))
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +110,7 @@ func TestVector(t *testing.T) {
 | 
			
		|||
			})
 | 
			
		||||
		},
 | 
			
		||||
		"sub": func(that *require.Assertions) {
 | 
			
		||||
			v := coll.From([]int{0, 1, 2, 3, 4, 5, 6})
 | 
			
		||||
			v := coll.From([]int{0, 1, 2, 3, 4, 5, 6}...)
 | 
			
		||||
 | 
			
		||||
			v1 := v.Clone().Sub(2, 4)
 | 
			
		||||
			that.Equal(2, len(v1))
 | 
			
		||||
| 
						 | 
				
			
			@ -139,13 +139,13 @@ func TestVector(t *testing.T) {
 | 
			
		|||
			})
 | 
			
		||||
		},
 | 
			
		||||
		"filter": func(that *require.Assertions) {
 | 
			
		||||
			v := coll.From([]int{1, 2, 3, 4, 5, 6})
 | 
			
		||||
			v := coll.From([]int{1, 2, 3, 4, 5, 6}...)
 | 
			
		||||
			v = v.Filter(func(i int) bool { return i%2 == 0 })
 | 
			
		||||
			that.Len(v, 3)
 | 
			
		||||
		},
 | 
			
		||||
		"any": func(that *require.Assertions) {
 | 
			
		||||
			that.True(coll.From([]int{1, 2, 3, 4, 5, 6}).Any(func(i int) bool { return i == 3 }))
 | 
			
		||||
			that.False(coll.From([]int{1, 2, 3, 4, 5, 6}).Any(func(i int) bool { return i == 666 }))
 | 
			
		||||
			that.True(coll.From([]int{1, 2, 3, 4, 5, 6}...).Any(func(i int) bool { return i == 3 }))
 | 
			
		||||
			that.False(coll.From([]int{1, 2, 3, 4, 5, 6}...).Any(func(i int) bool { return i == 666 }))
 | 
			
		||||
		},
 | 
			
		||||
		"take": func(that *require.Assertions) {
 | 
			
		||||
			that.EqualValues([]int{1, 2, 3}, coll.From([]int{1, 2, 3, 4, 5, 6}).Take(3))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,16 +3,22 @@ package chunk
 | 
			
		|||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"unicode"
 | 
			
		||||
	"unicode/utf8"
 | 
			
		||||
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/coll"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	escapeChar    = '\\'
 | 
			
		||||
	unicodeEscape = 'u'
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrNoMatch            = errors.New("no matching")
 | 
			
		||||
	ErrMapMemberNotString = errors.New("map member not a string")
 | 
			
		||||
	ErrUnexpectedSymbol   = errors.New("unexpected symbol")
 | 
			
		||||
	ErrMissingValue       = errors.New("missing value")
 | 
			
		||||
	ErrEscapeAtEnd        = errors.New("escape character at the end of value")
 | 
			
		||||
	ErrIncompleteEscape   = errors.New("incomplete unicode escape sequence")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Chunk struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -24,72 +30,120 @@ type Chunk struct {
 | 
			
		|||
	globRight int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) LeftByte() byte {
 | 
			
		||||
	return c.vector[c.posLeft]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) Null() bool {
 | 
			
		||||
	return (c.posRight+1)-c.posLeft >= 4 && c.vector[c.posLeft] == 'n' && c.vector[c.posLeft+1] == 'u' && c.vector[c.posLeft+2] == 'l' && c.vector[c.posLeft+3] == 'l'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) LeftPosf(format string, args ...any) error {
 | 
			
		||||
	return fmt.Errorf(fmt.Sprintf("[%d] %s", c.globLeft, format), args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match finds the matching closer to the symbol at the left position and sets the
 | 
			
		||||
// right position to this index
 | 
			
		||||
func (c Chunk) Match() (Chunk, error) {
 | 
			
		||||
	s, ok := matchers[c.vector[c.posLeft]]
 | 
			
		||||
	start := c.vector[c.posLeft]
 | 
			
		||||
	matcher, ok := matchers[start]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		panic(fmt.Sprintf("Match called on %c with no matcher defined", c.vector[c.posLeft]))
 | 
			
		||||
	}
 | 
			
		||||
	if s.StartFromRight {
 | 
			
		||||
		for c.posRight > 0 {
 | 
			
		||||
			if c.vector[c.posRight] == s.MatchByte {
 | 
			
		||||
				return c, nil
 | 
			
		||||
 | 
			
		||||
	for index := c.posLeft + 1; index < len(c.vector); index++ {
 | 
			
		||||
		if start != '"' && c.vector[index] == start {
 | 
			
		||||
			sub, err := c.Child(index, len(c.vector)).Match()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return c, fmt.Errorf("[%d] child %w", c.globLeft+(c.posLeft-index), err)
 | 
			
		||||
			}
 | 
			
		||||
			c.posRight--
 | 
			
		||||
			c.globRight--
 | 
			
		||||
			index += sub.posRight
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		for index := c.posLeft + 1; index < len(c.vector); index++ {
 | 
			
		||||
			if c.vector[index] == s.MatchByte {
 | 
			
		||||
				c.posRight = index
 | 
			
		||||
				c.globRight += (c.posLeft + 1) - index
 | 
			
		||||
				return c, nil
 | 
			
		||||
		if c.vector[index] == escapeChar {
 | 
			
		||||
			if index+1 == len(c.vector) {
 | 
			
		||||
				return c, fmt.Errorf("[%d] %w", c.globLeft+(c.posLeft-index), ErrEscapeAtEnd)
 | 
			
		||||
			}
 | 
			
		||||
			if c.vector[index+1] == unicodeEscape {
 | 
			
		||||
				if index+6 >= len(c.vector) {
 | 
			
		||||
					return c, fmt.Errorf("[%d] %w", c.globLeft+(c.posLeft+index), ErrIncompleteEscape)
 | 
			
		||||
				}
 | 
			
		||||
				index += 5
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			index++
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if c.vector[index] == matcher {
 | 
			
		||||
			c.globRight -= c.posRight - index
 | 
			
		||||
			c.posRight = index
 | 
			
		||||
			return c, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return c, fmt.Errorf("%w %c", ErrNoMatch, c.vector[c.posLeft])
 | 
			
		||||
	return c, c.LeftPosf("%w %c", ErrNoMatch, c.vector[c.posLeft])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) Copy() Chunk {
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) Left() byte {
 | 
			
		||||
	return c.vector[c.posLeft]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sub returns an inclusive subchunk of [left;right]
 | 
			
		||||
func (c Chunk) Sub() Chunk {
 | 
			
		||||
	return New(c.vector[c.posLeft : c.posRight+1])
 | 
			
		||||
	c = c.Child(c.posLeft, c.posRight+1)
 | 
			
		||||
	// To be inclusive we incremented posRight above, so
 | 
			
		||||
	// to restore the original position, we must decrement
 | 
			
		||||
	// it here before returning. Only if we have room to decrement it
 | 
			
		||||
	if c.posRight > 0 {
 | 
			
		||||
		c.posRight--
 | 
			
		||||
		c.globRight--
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CookieCutter returns a subchunk of (left;right)
 | 
			
		||||
func (c Chunk) CookieCutter() Chunk {
 | 
			
		||||
	return New(c.vector[c.posLeft+1 : c.posRight])
 | 
			
		||||
	return c.Child(c.posLeft+1, c.posRight)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func spaceAtIndex(v coll.Vector[byte], index int) bool {
 | 
			
		||||
	return v[index] == 0x9 || v[index] == 0xa || v[index] == 0xd || v[index] == 0x20
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) AtSpace() bool {
 | 
			
		||||
	r, _ := utf8.DecodeRune(c.vector[c.posLeft:])
 | 
			
		||||
	return unicode.IsSpace(r)
 | 
			
		||||
	return spaceAtIndex(c.vector, c.posLeft)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) Seek() Chunk {
 | 
			
		||||
	for c.posLeft < len(c.vector) && c.AtSpace() {
 | 
			
		||||
		c.globLeft++
 | 
			
		||||
		c.posLeft++
 | 
			
		||||
	}
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Step increments the left position by 1 if b
 | 
			
		||||
// is the byte at the current left position
 | 
			
		||||
func (c Chunk) StepIf(b byte) Chunk {
 | 
			
		||||
	if c.vector[c.posLeft] != b {
 | 
			
		||||
		return c
 | 
			
		||||
	}
 | 
			
		||||
	c.posLeft++
 | 
			
		||||
	c.globLeft++
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) ValueEnd() (Chunk, error) {
 | 
			
		||||
	switch c.vector[c.posLeft] {
 | 
			
		||||
	case '"', '{', '[':
 | 
			
		||||
		return c.Match()
 | 
			
		||||
	default:
 | 
			
		||||
		for index := c.posLeft; index <= c.posRight; index++ {
 | 
			
		||||
			if c.vector[index] == ',' {
 | 
			
		||||
				return New(c.vector[c.posLeft:index]), nil
 | 
			
		||||
			if c.vector[index] == ',' || spaceAtIndex(c.vector, index) {
 | 
			
		||||
				if index == c.posLeft {
 | 
			
		||||
					return c, c.LeftPosf("%w", ErrMissingValue)
 | 
			
		||||
				}
 | 
			
		||||
				return c.Child(c.posLeft, index), nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return c, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +154,7 @@ func (c Chunk) ValueEnd() (Chunk, error) {
 | 
			
		|||
func (c Chunk) Skip() Chunk {
 | 
			
		||||
	if c.posLeft+1 < len(c.vector) {
 | 
			
		||||
		c.posLeft++
 | 
			
		||||
		c.globLeft++
 | 
			
		||||
	}
 | 
			
		||||
	return c.Seek()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -110,18 +165,10 @@ type MatchRule struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	matchers = map[byte]MatchRule{
 | 
			
		||||
		'{': {
 | 
			
		||||
			MatchByte:      '}',
 | 
			
		||||
			StartFromRight: true,
 | 
			
		||||
		},
 | 
			
		||||
		'[': {
 | 
			
		||||
			MatchByte:      ']',
 | 
			
		||||
			StartFromRight: true,
 | 
			
		||||
		},
 | 
			
		||||
		'"': {
 | 
			
		||||
			MatchByte: '"',
 | 
			
		||||
		},
 | 
			
		||||
	matchers = map[byte]byte{
 | 
			
		||||
		'{': '}',
 | 
			
		||||
		'[': ']',
 | 
			
		||||
		'"': '"',
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -131,9 +178,10 @@ func New(v []byte) Chunk {
 | 
			
		|||
		posRight = 0
 | 
			
		||||
	}
 | 
			
		||||
	return Chunk{
 | 
			
		||||
		vector:   coll.Vector[byte](v),
 | 
			
		||||
		posLeft:  0,
 | 
			
		||||
		posRight: posRight,
 | 
			
		||||
		vector:    coll.Vector[byte](v),
 | 
			
		||||
		posLeft:   0,
 | 
			
		||||
		posRight:  posRight,
 | 
			
		||||
		globRight: posRight,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -142,31 +190,14 @@ func (c Chunk) Child(left, right int) Chunk {
 | 
			
		|||
	return Chunk{
 | 
			
		||||
		vector:    sub,
 | 
			
		||||
		posLeft:   0,
 | 
			
		||||
		posRight:  len(sub),
 | 
			
		||||
		posRight:  len(sub) - 1,
 | 
			
		||||
		globLeft:  (c.globLeft - c.posLeft) + left,
 | 
			
		||||
		globRight: (c.globRight - c.posRight) + right,
 | 
			
		||||
		globRight: (c.globRight - c.posRight) + (right - 1),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ParseFunc[T any] func(T) (T, error)
 | 
			
		||||
 | 
			
		||||
func Parse[T any](c Chunk) ParseFunc[T] {
 | 
			
		||||
	switch c.vector[c.posLeft] {
 | 
			
		||||
	case '{':
 | 
			
		||||
		return ParseMap[T](c.CookieCutter())
 | 
			
		||||
	default:
 | 
			
		||||
		panic("not implemented")
 | 
			
		||||
	}
 | 
			
		||||
	// return can be:
 | 
			
		||||
	// * {}
 | 
			
		||||
	// * []
 | 
			
		||||
	// * ""
 | 
			
		||||
	// * 123
 | 
			
		||||
	// * true
 | 
			
		||||
	// * false
 | 
			
		||||
	// * null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Row struct {
 | 
			
		||||
	Name  string
 | 
			
		||||
	Value Chunk
 | 
			
		||||
| 
						 | 
				
			
			@ -177,47 +208,47 @@ func (c Chunk) String() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c Chunk) Row() (Row, error) {
 | 
			
		||||
	c = c.Seek()
 | 
			
		||||
	if c.vector[c.posLeft] != '"' {
 | 
			
		||||
		return Row{}, fmt.Errorf("%w: %c", ErrMapMemberNotString, c.vector[c.posLeft])
 | 
			
		||||
		return Row{}, c.LeftPosf("%w: %c", ErrMapMemberNotString, c.vector[c.posLeft])
 | 
			
		||||
	}
 | 
			
		||||
	name, err := c.Match()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Row{}, fmt.Errorf("match: %w", err)
 | 
			
		||||
		return Row{}, c.LeftPosf("match: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	postName := c.Copy()
 | 
			
		||||
	postName.posLeft = name.posRight
 | 
			
		||||
	postName.globLeft = name.globRight
 | 
			
		||||
	postName = postName.Skip()
 | 
			
		||||
	// Next we must get a :
 | 
			
		||||
	if postName.vector[postName.posLeft] != ':' {
 | 
			
		||||
		return Row{}, fmt.Errorf("%w '%c', expected ':'", ErrUnexpectedSymbol, postName.vector[postName.posLeft])
 | 
			
		||||
		return Row{}, postName.LeftPosf("%w '%c', expected ':'", ErrUnexpectedSymbol, postName.vector[postName.posLeft])
 | 
			
		||||
	}
 | 
			
		||||
	value, err := postName.Skip().ValueEnd()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Row{}, fmt.Errorf("value: %w", err)
 | 
			
		||||
		return Row{}, postName.LeftPosf("value: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Row{
 | 
			
		||||
		Name:  name.String(),
 | 
			
		||||
		Name:  name.String()[1 : name.posRight-name.posLeft],
 | 
			
		||||
		Value: value,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// After returns the chunk with its left position, if possible,
 | 
			
		||||
// right after the global right position of v
 | 
			
		||||
func (c Chunk) After(v Chunk) Chunk {
 | 
			
		||||
	c.posLeft = v.posLeft
 | 
			
		||||
	// Add two, as one is for dealing with right side being exclusive
 | 
			
		||||
	// in slice indexes, and another one to go on to the next
 | 
			
		||||
	offset := (v.globRight - c.globLeft) + 1
 | 
			
		||||
	// Then, make sure we don't go too far
 | 
			
		||||
	if c.posLeft+offset >= len(c.vector) {
 | 
			
		||||
		offset--
 | 
			
		||||
	}
 | 
			
		||||
	c.posLeft += offset
 | 
			
		||||
	c.globLeft += offset
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ParseMap[T any](c Chunk) ParseFunc[T] {
 | 
			
		||||
	return func(t T) (T, error) {
 | 
			
		||||
		// mapper := parse.GetMap(t)
 | 
			
		||||
		// for {
 | 
			
		||||
		// 	row, err := c.Row()
 | 
			
		||||
		// 	if err != nil {
 | 
			
		||||
		// 		return t, err
 | 
			
		||||
		// 	}
 | 
			
		||||
 | 
			
		||||
		// }
 | 
			
		||||
		panic("todo")
 | 
			
		||||
	}
 | 
			
		||||
func (c Chunk) EOF() bool {
 | 
			
		||||
	return c.posLeft >= len(c.vector)-1
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,37 +1,108 @@
 | 
			
		|||
package chunk_test
 | 
			
		||||
package chunk
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/ld/internal/parse/chunk"
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/coll"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSpace(t *testing.T) {
 | 
			
		||||
	tests := []byte{0x9, 0xa, 0xd, 0x20}
 | 
			
		||||
 | 
			
		||||
	that := require.New(t)
 | 
			
		||||
	ch := chunk.New([]byte(" hello world"))
 | 
			
		||||
	that.True(ch.AtSpace())
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		b := []byte("xhello world")
 | 
			
		||||
		b[0] = test
 | 
			
		||||
		that.True(New(b).AtSpace())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseMap(t *testing.T) {
 | 
			
		||||
	type Hello struct {
 | 
			
		||||
		Hello string
 | 
			
		||||
func TestAlign(t *testing.T) {
 | 
			
		||||
	tests := map[string]struct {
 | 
			
		||||
		c func() Chunk
 | 
			
		||||
		v func(Chunk) Chunk
 | 
			
		||||
	}{
 | 
			
		||||
		"same-size": {
 | 
			
		||||
			c: func() Chunk {
 | 
			
		||||
				c := New(coll.Vector[byte](`{"heres some test data": "hi mom"}`))
 | 
			
		||||
				c.posLeft = 15
 | 
			
		||||
				c.globLeft = 15
 | 
			
		||||
				return c
 | 
			
		||||
			},
 | 
			
		||||
			v: func(c Chunk) Chunk {
 | 
			
		||||
				c.posLeft = 20
 | 
			
		||||
				c.globLeft = 20
 | 
			
		||||
				c.globRight = 22
 | 
			
		||||
				c.posRight = 22
 | 
			
		||||
				return c
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"cookie-cutter": {
 | 
			
		||||
			c: func() Chunk {
 | 
			
		||||
				c := New(coll.Vector[byte](`{"heres some test data": "hi mom"}`))
 | 
			
		||||
				c.posLeft = 15
 | 
			
		||||
				c.globLeft = 15
 | 
			
		||||
				return c
 | 
			
		||||
			},
 | 
			
		||||
			v: func(c Chunk) Chunk {
 | 
			
		||||
				return c.CookieCutter().CookieCutter()
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"no-change": {
 | 
			
		||||
			c: func() Chunk {
 | 
			
		||||
				c := New(coll.Vector[byte](`{"heres some test data": "hi mom"}`))
 | 
			
		||||
				c.posLeft = 15
 | 
			
		||||
				c.globLeft = 15
 | 
			
		||||
				return c
 | 
			
		||||
			},
 | 
			
		||||
			v: func(c Chunk) Chunk {
 | 
			
		||||
				c.posRight = 15
 | 
			
		||||
				c.globRight = 15
 | 
			
		||||
				return c.Sub()
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		"contains": {
 | 
			
		||||
			c: func() Chunk {
 | 
			
		||||
				return New(coll.Vector[byte](`0123456789`))
 | 
			
		||||
			},
 | 
			
		||||
			v: func(c Chunk) Chunk {
 | 
			
		||||
				return c.Child(5, 8)
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	ch := chunk.New([]byte(`"Hello": "world"`))
 | 
			
		||||
	h, err := chunk.ParseMap[Hello](ch)(Hello{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
 | 
			
		||||
	for name, test := range tests {
 | 
			
		||||
		test := test
 | 
			
		||||
		t.Run(name, func(tt *testing.T) {
 | 
			
		||||
			that := require.New(tt)
 | 
			
		||||
			c := test.c()
 | 
			
		||||
			v := test.v(c)
 | 
			
		||||
			out := c.After(v)
 | 
			
		||||
			that.Equal(v.globRight+1, out.globLeft)
 | 
			
		||||
			that.Equal(string(c.vector[v.globRight+1]), string(out.LeftByte()))
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println(h)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestChild(t *testing.T) {
 | 
			
		||||
	type Hello struct {
 | 
			
		||||
		Hello string
 | 
			
		||||
	}
 | 
			
		||||
	ch := chunk.New([]byte(`"Hello": "world"`))
 | 
			
		||||
	child := ch.Child(5, 10)
 | 
			
		||||
 | 
			
		||||
	fmt.Println(child)
 | 
			
		||||
func TestRow(t *testing.T) {
 | 
			
		||||
	c := New(coll.Vector[byte](`{
 | 
			
		||||
		"heres some test data": "hi mom",
 | 
			
		||||
		"another field": "get it right",
 | 
			
		||||
		"integer": -12345
 | 
			
		||||
	}`))
 | 
			
		||||
	that := require.New(t)
 | 
			
		||||
	row, err := c.CookieCutter().Seek().Row()
 | 
			
		||||
	that.NoError(err)
 | 
			
		||||
	that.Equal("heres some test data", row.Name)
 | 
			
		||||
	that.Equal(`"hi mom"`, row.Value.String())
 | 
			
		||||
	row, err = c.After(row.Value).StepIf(',').Seek().Row()
 | 
			
		||||
	that.NoError(err)
 | 
			
		||||
	that.Equal("another field", row.Name)
 | 
			
		||||
	that.Equal(`"get it right"`, row.Value.String())
 | 
			
		||||
	row, err = c.After(row.Value).StepIf(',').Seek().Row()
 | 
			
		||||
	that.NoError(err)
 | 
			
		||||
	that.Equal("integer", row.Name)
 | 
			
		||||
	that.Equal("-12345", row.Value.String())
 | 
			
		||||
	that.True(c.After(row.Value).Seek().EOF())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,54 +1,200 @@
 | 
			
		|||
package parse
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/coll"
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/ld/internal/consts"
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/ld/internal/parse/chunk"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type LazyMapFunc func(name string, value string)
 | 
			
		||||
const (
 | 
			
		||||
	memberSeparator = ','
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func GetMap(v any) LazyMapFunc {
 | 
			
		||||
	val := reflect.ValueOf(v)
 | 
			
		||||
	// typ := reflect.TypeOf(v)
 | 
			
		||||
	switch val.Kind() {
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		return func(name string, value string) {
 | 
			
		||||
			// val.SetMapIndex(reflect.ValueOf(name), value)
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		// fields := GetStructFields(val, typ)
 | 
			
		||||
		return func(name, value string) {
 | 
			
		||||
			// val, ok := fields[name]
 | 
			
		||||
			// if ok {
 | 
			
		||||
			// val.Set(value)
 | 
			
		||||
			// }
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		panic("wrong")
 | 
			
		||||
var (
 | 
			
		||||
	ErrNotArray = errors.New("value is not an array")
 | 
			
		||||
	ErrNotMap   = errors.New("value is not a map")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	boolVals = map[string]bool{
 | 
			
		||||
		"true":  true,
 | 
			
		||||
		"false": false,
 | 
			
		||||
	}
 | 
			
		||||
	determineMap = func() map[byte]coll.Vector[reflect.Kind] {
 | 
			
		||||
		m := map[byte]coll.Vector[reflect.Kind]{
 | 
			
		||||
			'"': coll.From(reflect.String),
 | 
			
		||||
			'[': coll.From(reflect.Array, reflect.Slice),
 | 
			
		||||
			'{': coll.From(reflect.Map, reflect.Struct),
 | 
			
		||||
			'-': coll.From(reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64),
 | 
			
		||||
			't': coll.From(reflect.Bool),
 | 
			
		||||
			'f': coll.From(reflect.Bool),
 | 
			
		||||
		}
 | 
			
		||||
		numbers := coll.From(
 | 
			
		||||
			reflect.Int,
 | 
			
		||||
			reflect.Int8,
 | 
			
		||||
			reflect.Int16,
 | 
			
		||||
			reflect.Int32,
 | 
			
		||||
			reflect.Int64,
 | 
			
		||||
			reflect.Uint,
 | 
			
		||||
			reflect.Uint64,
 | 
			
		||||
			reflect.Uint32,
 | 
			
		||||
			reflect.Uint16,
 | 
			
		||||
			reflect.Uint8,
 | 
			
		||||
			reflect.Float32,
 | 
			
		||||
			reflect.Float64,
 | 
			
		||||
		)
 | 
			
		||||
		for index := byte('0'); index <= '9'; index++ {
 | 
			
		||||
			m[index] = numbers
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return m
 | 
			
		||||
	}()
 | 
			
		||||
	sizes = map[reflect.Kind]int{
 | 
			
		||||
		// Int and Uint values stolen from the [math] package, as the intSize
 | 
			
		||||
		// constant is unexported
 | 
			
		||||
		reflect.Int:     32 << (^uint(0) >> 63), // 32 or 64
 | 
			
		||||
		reflect.Uint:    32 << (^uint(0) >> 63), // 32 or 64
 | 
			
		||||
		reflect.Int64:   64,
 | 
			
		||||
		reflect.Int32:   32,
 | 
			
		||||
		reflect.Int16:   16,
 | 
			
		||||
		reflect.Int8:    8,
 | 
			
		||||
		reflect.Uint64:  64,
 | 
			
		||||
		reflect.Uint32:  32,
 | 
			
		||||
		reflect.Uint16:  16,
 | 
			
		||||
		reflect.Uint8:   8,
 | 
			
		||||
		reflect.Float32: 32,
 | 
			
		||||
		reflect.Float64: 64,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func SetMap(val reflect.Value, typ reflect.Type, c chunk.Chunk) error {
 | 
			
		||||
	if c.LeftByte() != '{' {
 | 
			
		||||
		return c.LeftPosf("%w", ErrNotMap)
 | 
			
		||||
	}
 | 
			
		||||
	fields := GetStructFields(val, typ)
 | 
			
		||||
	c = c.CookieCutter().Seek()
 | 
			
		||||
	for !c.EOF() {
 | 
			
		||||
		row, err := c.Row()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("row: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		field, ok := fields[row.Name]
 | 
			
		||||
		if ok {
 | 
			
		||||
			if err := SetValue(field, field.Type(), row.Value); err != nil {
 | 
			
		||||
				return fmt.Errorf("set: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Shift c
 | 
			
		||||
		c = c.After(row.Value).StepIf(memberSeparator).Seek()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ParseAsValue(v string, valType reflect.Type) (any, error) {
 | 
			
		||||
	// Might not be necessary to trim
 | 
			
		||||
	v = strings.TrimSpace(v)
 | 
			
		||||
	switch valType.Kind() {
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		return v[1 : len(v)-1], nil
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		b, err := strconv.ParseBool(strings.ToLower(v))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Errorf("boolean: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		return b, nil
 | 
			
		||||
	case reflect.Struct, reflect.Map:
 | 
			
		||||
		panic("todo")
 | 
			
		||||
	default:
 | 
			
		||||
		panic("todo")
 | 
			
		||||
func SetArray(val reflect.Value, typ reflect.Type, c chunk.Chunk) error {
 | 
			
		||||
	if c.LeftByte() != '[' {
 | 
			
		||||
		return c.LeftPosf("%w", ErrNotArray)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c = c.CookieCutter().Seek()
 | 
			
		||||
	elems := coll.New[chunk.Chunk]()
 | 
			
		||||
	for !c.EOF() {
 | 
			
		||||
		element, err := c.ValueEnd()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("getting array elem: %w", err)
 | 
			
		||||
		}
 | 
			
		||||
		elems = elems.Push(element)
 | 
			
		||||
		c = c.After(element).StepIf(memberSeparator).Seek()
 | 
			
		||||
	}
 | 
			
		||||
	elementType := typ.Elem()
 | 
			
		||||
	arrayType := reflect.ArrayOf(len(elems), elementType)
 | 
			
		||||
	array := reflect.New(arrayType).Elem()
 | 
			
		||||
	for index, elem := range elems {
 | 
			
		||||
		if err := SetValue(array.Index(index), elementType, elem); err != nil {
 | 
			
		||||
			return fmt.Errorf("%w at array index [%d]", err, index)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if val.Kind() == reflect.Slice {
 | 
			
		||||
		array = array.Slice(0, len(elems))
 | 
			
		||||
	}
 | 
			
		||||
	val.Set(array)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Determines what [reflect.Kind] of object the given [chunk.Chunk] is
 | 
			
		||||
func Determine(c chunk.Chunk) coll.Vector[reflect.Kind] {
 | 
			
		||||
	k, ok := determineMap[c.LeftByte()]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return coll.From(reflect.Invalid)
 | 
			
		||||
	}
 | 
			
		||||
	return k
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetValue(val reflect.Value, typ reflect.Type, c chunk.Chunk) error {
 | 
			
		||||
	if c.Null() {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if val.Kind() == reflect.Pointer {
 | 
			
		||||
		if val.IsNil() {
 | 
			
		||||
			val.Set(reflect.New(typ.Elem()))
 | 
			
		||||
		}
 | 
			
		||||
		return SetValue(val.Elem(), typ.Elem(), c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kinds := Determine(c)
 | 
			
		||||
	if !coll.AnyOf(kinds, val.Kind()) {
 | 
			
		||||
		return fmt.Errorf("kind %s not found in appropriate list %s for value [%s]", val.Kind(), kinds, c)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch val.Kind() {
 | 
			
		||||
	case reflect.Map, reflect.Struct:
 | 
			
		||||
		return SetMap(val, typ, c)
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		val.SetString(Escape(c.CookieCutter()))
 | 
			
		||||
	case reflect.Bool:
 | 
			
		||||
		b, ok := boolVals[c.String()]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return fmt.Errorf("value [%s] is not a boolean", c.String())
 | 
			
		||||
		}
 | 
			
		||||
		val.SetBool(b)
 | 
			
		||||
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
		i, err := strconv.ParseInt(c.String(), 10, sizes[val.Kind()])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("value [%s] is not an %s", c.String(), val.Kind().String())
 | 
			
		||||
		}
 | 
			
		||||
		val.SetInt(i)
 | 
			
		||||
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
			
		||||
		u, err := strconv.ParseUint(c.String(), 10, sizes[val.Kind()])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("value [%s] is not an %s", c.String(), val.Kind().String())
 | 
			
		||||
		}
 | 
			
		||||
		val.SetUint(u)
 | 
			
		||||
	case reflect.Float32, reflect.Float64:
 | 
			
		||||
		f, err := strconv.ParseFloat(c.String(), sizes[val.Kind()])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("value [%s] is not an %s", c.String(), val.Kind().String())
 | 
			
		||||
		}
 | 
			
		||||
		val.SetFloat(f)
 | 
			
		||||
	case reflect.Array, reflect.Slice:
 | 
			
		||||
		return SetArray(val, typ, c)
 | 
			
		||||
	default:
 | 
			
		||||
		fmt.Println(val.Kind().String())
 | 
			
		||||
		panic("unsupported")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Escape(v chunk.Chunk) string {
 | 
			
		||||
	// TODO: string escaping
 | 
			
		||||
	return v.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func GetStructFields(val reflect.Value, typ reflect.Type) map[string]reflect.Value {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +1,17 @@
 | 
			
		|||
package ld
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/ld/internal/parse"
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/ld/internal/parse/chunk"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Unmarshal[T any](v []byte) (T, error) {
 | 
			
		||||
	ptr := new(T)
 | 
			
		||||
	if err := parse.SetValue(reflect.ValueOf(ptr), reflect.TypeOf(ptr), chunk.New(v)); err != nil {
 | 
			
		||||
		return *ptr, fmt.Errorf("unmarshal: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	return *ptr, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,192 +1,192 @@
 | 
			
		|||
package ld_test
 | 
			
		||||
 | 
			
		||||
// import (
 | 
			
		||||
// 	"encoding/json"
 | 
			
		||||
// 	"fmt"
 | 
			
		||||
// 	"math"
 | 
			
		||||
// 	"testing"
 | 
			
		||||
// 	"time"
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
// 	"sectorinf.com/emilis/flabk/pkg/asld"
 | 
			
		||||
// 	"sectorinf.com/emilis/flabk/pkg/epk"
 | 
			
		||||
// 	"github.com/stretchr/testify/require"
 | 
			
		||||
// )
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/epk"
 | 
			
		||||
	"sectorinf.com/emilis/flabk/pkg/ld"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// type testObj struct {
 | 
			
		||||
// 	String  string `asld:"string" json:"string"`
 | 
			
		||||
// 	NoTag   string
 | 
			
		||||
// 	Int     int
 | 
			
		||||
// 	Int8    int8
 | 
			
		||||
// 	Int16   int16
 | 
			
		||||
// 	Int32   int32
 | 
			
		||||
// 	Int64   int64
 | 
			
		||||
// 	Uint    uint
 | 
			
		||||
// 	Uint8   uint8
 | 
			
		||||
// 	Uint16  uint16
 | 
			
		||||
// 	Uint32  uint32
 | 
			
		||||
// 	Uint64  uint64
 | 
			
		||||
// 	Float32 float32
 | 
			
		||||
// 	Float64 float64
 | 
			
		||||
// 	// Complex64     complex64
 | 
			
		||||
// 	// Complex128    complex128
 | 
			
		||||
// 	IntPtr     *int
 | 
			
		||||
// 	Int8Ptr    *int8
 | 
			
		||||
// 	Int16Ptr   *int16
 | 
			
		||||
// 	Int32Ptr   *int32
 | 
			
		||||
// 	Int64Ptr   *int64
 | 
			
		||||
// 	UintPtr    *uint
 | 
			
		||||
// 	Uint8Ptr   *uint8
 | 
			
		||||
// 	Uint16Ptr  *uint16
 | 
			
		||||
// 	Uint32Ptr  *uint32
 | 
			
		||||
// 	Uint64Ptr  *uint64
 | 
			
		||||
// 	Float32Ptr *float32
 | 
			
		||||
// 	Float64Ptr *float64
 | 
			
		||||
// 	// Complex64Ptr  *complex64
 | 
			
		||||
// 	// Complex128Ptr *complex128
 | 
			
		||||
// 	TestPtr   *testObj
 | 
			
		||||
// 	TestArray []testObj
 | 
			
		||||
// }
 | 
			
		||||
type testObj struct {
 | 
			
		||||
	String  string `ld:"string" json:"string"`
 | 
			
		||||
	NoTag   string
 | 
			
		||||
	Int     int
 | 
			
		||||
	Int8    int8
 | 
			
		||||
	Int16   int16
 | 
			
		||||
	Int32   int32
 | 
			
		||||
	Int64   int64
 | 
			
		||||
	Uint    uint
 | 
			
		||||
	Uint8   uint8
 | 
			
		||||
	Uint16  uint16
 | 
			
		||||
	Uint32  uint32
 | 
			
		||||
	Uint64  uint64
 | 
			
		||||
	Float32 float32
 | 
			
		||||
	Float64 float64
 | 
			
		||||
	// Complex64     complex64
 | 
			
		||||
	// Complex128    complex128
 | 
			
		||||
	IntPtr     *int
 | 
			
		||||
	Int8Ptr    *int8
 | 
			
		||||
	Int16Ptr   *int16
 | 
			
		||||
	Int32Ptr   *int32
 | 
			
		||||
	Int64Ptr   *int64
 | 
			
		||||
	UintPtr    *uint
 | 
			
		||||
	Uint8Ptr   *uint8
 | 
			
		||||
	Uint16Ptr  *uint16
 | 
			
		||||
	Uint32Ptr  *uint32
 | 
			
		||||
	Uint64Ptr  *uint64
 | 
			
		||||
	Float32Ptr *float32
 | 
			
		||||
	Float64Ptr *float64
 | 
			
		||||
	// Complex64Ptr  *complex64
 | 
			
		||||
	// Complex128Ptr *complex128
 | 
			
		||||
	TestPtr   *testObj
 | 
			
		||||
	TestArray []testObj
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func TestUnmarshal(t *testing.T) {
 | 
			
		||||
// 	tests := map[string]struct {
 | 
			
		||||
// 		obj      testObj
 | 
			
		||||
// 		errCheck epk.ErrorTest
 | 
			
		||||
// 	}{
 | 
			
		||||
// 		"string children": {
 | 
			
		||||
// 			obj: testObj{
 | 
			
		||||
// 				String:  "hello",
 | 
			
		||||
// 				NoTag:   "no_tag",
 | 
			
		||||
// 				Int:     math.MaxInt,
 | 
			
		||||
// 				Int8:    math.MaxInt8,
 | 
			
		||||
// 				Int16:   math.MaxInt16,
 | 
			
		||||
// 				Int32:   math.MaxInt32,
 | 
			
		||||
// 				Int64:   math.MaxInt64,
 | 
			
		||||
// 				Uint:    math.MaxUint,
 | 
			
		||||
// 				Uint8:   math.MaxUint8,
 | 
			
		||||
// 				Uint16:  math.MaxUint16,
 | 
			
		||||
// 				Uint32:  math.MaxUint32,
 | 
			
		||||
// 				Uint64:  math.MaxUint64,
 | 
			
		||||
// 				Float32: math.MaxFloat32,
 | 
			
		||||
// 				Float64: math.MaxFloat64,
 | 
			
		||||
// 				TestPtr: &testObj{
 | 
			
		||||
// 					String: "hello2",
 | 
			
		||||
// 				},
 | 
			
		||||
// 				TestArray: []testObj{
 | 
			
		||||
// 					{
 | 
			
		||||
// 						String: "hello3",
 | 
			
		||||
// 					},
 | 
			
		||||
// 					{
 | 
			
		||||
// 						String: "hello4",
 | 
			
		||||
// 						TestPtr: &testObj{
 | 
			
		||||
// 							TestArray: []testObj{
 | 
			
		||||
// 								{
 | 
			
		||||
// 									String: "hello5",
 | 
			
		||||
// 								},
 | 
			
		||||
// 							},
 | 
			
		||||
// 						},
 | 
			
		||||
// 					},
 | 
			
		||||
// 				},
 | 
			
		||||
// 				// Complex64:  complex(math.MaxFloat32, math.MaxFloat32),
 | 
			
		||||
// 				// Complex128: complex(math.MaxFloat64, math.MaxFloat64),
 | 
			
		||||
// 			},
 | 
			
		||||
// 		},
 | 
			
		||||
// 	}
 | 
			
		||||
func TestUnmarshal(t *testing.T) {
 | 
			
		||||
	tests := map[string]struct {
 | 
			
		||||
		obj      testObj
 | 
			
		||||
		errCheck epk.ErrorTest
 | 
			
		||||
	}{
 | 
			
		||||
		"string children": {
 | 
			
		||||
			obj: testObj{
 | 
			
		||||
				String:  "hello",
 | 
			
		||||
				NoTag:   "no_tag",
 | 
			
		||||
				Int:     math.MaxInt,
 | 
			
		||||
				Int8:    math.MaxInt8,
 | 
			
		||||
				Int16:   math.MaxInt16,
 | 
			
		||||
				Int32:   math.MaxInt32,
 | 
			
		||||
				Int64:   math.MaxInt64,
 | 
			
		||||
				Uint:    math.MaxUint,
 | 
			
		||||
				Uint8:   math.MaxUint8,
 | 
			
		||||
				Uint16:  math.MaxUint16,
 | 
			
		||||
				Uint32:  math.MaxUint32,
 | 
			
		||||
				Uint64:  math.MaxUint64,
 | 
			
		||||
				Float32: math.MaxFloat32,
 | 
			
		||||
				Float64: math.MaxFloat64,
 | 
			
		||||
				TestPtr: &testObj{
 | 
			
		||||
					String: "hello2",
 | 
			
		||||
				},
 | 
			
		||||
				TestArray: []testObj{
 | 
			
		||||
					{
 | 
			
		||||
						String: "hello3",
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						String: "hello4",
 | 
			
		||||
						TestPtr: &testObj{
 | 
			
		||||
							TestArray: []testObj{
 | 
			
		||||
								{
 | 
			
		||||
									String: "hello5",
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
				// Complex64:  complex(math.MaxFloat32, math.MaxFloat32),
 | 
			
		||||
				// Complex128: complex(math.MaxFloat64, math.MaxFloat64),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
// 	for name, test := range tests {
 | 
			
		||||
// 		test := test
 | 
			
		||||
// 		t.Run(name, func(tt *testing.T) {
 | 
			
		||||
// 			that := require.New(tt)
 | 
			
		||||
// 			objJSON, err := json.MarshalIndent(test.obj, "", "  ")
 | 
			
		||||
// 			that.NoError(err)
 | 
			
		||||
// 			result, err := asld.Unmarshal[testObj](objJSON)
 | 
			
		||||
// 			that.NoError(err)
 | 
			
		||||
// 			that.Equal(test.obj, result)
 | 
			
		||||
// 		})
 | 
			
		||||
// 	}
 | 
			
		||||
// }
 | 
			
		||||
	for name, test := range tests {
 | 
			
		||||
		test := test
 | 
			
		||||
		t.Run(name, func(tt *testing.T) {
 | 
			
		||||
			that := require.New(tt)
 | 
			
		||||
			objJSON, err := json.MarshalIndent(test.obj, "", "  ")
 | 
			
		||||
			that.NoError(err)
 | 
			
		||||
			result, err := ld.Unmarshal[testObj](objJSON)
 | 
			
		||||
			that.NoError(err)
 | 
			
		||||
			that.Equal(test.obj, result)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func TestBench(t *testing.T) {
 | 
			
		||||
// 	obj := testObj{
 | 
			
		||||
// 		String:  "hello",
 | 
			
		||||
// 		NoTag:   "no_tag",
 | 
			
		||||
// 		Int:     math.MaxInt,
 | 
			
		||||
// 		Int8:    math.MaxInt8,
 | 
			
		||||
// 		Int16:   math.MaxInt16,
 | 
			
		||||
// 		Int32:   math.MaxInt32,
 | 
			
		||||
// 		Int64:   math.MaxInt64,
 | 
			
		||||
// 		Uint:    math.MaxUint,
 | 
			
		||||
// 		Uint8:   math.MaxUint8,
 | 
			
		||||
// 		Uint16:  math.MaxUint16,
 | 
			
		||||
// 		Uint32:  math.MaxUint32,
 | 
			
		||||
// 		Uint64:  math.MaxUint64,
 | 
			
		||||
// 		Float32: math.MaxFloat32,
 | 
			
		||||
// 		Float64: math.MaxFloat64,
 | 
			
		||||
// 		// Complex64:  complex(math.MaxFloat32, math.MaxFloat32),
 | 
			
		||||
// 		// Complex128: complex(math.MaxFloat64, math.MaxFloat64),
 | 
			
		||||
// 	}
 | 
			
		||||
// 	that := require.New(t)
 | 
			
		||||
// 	asldTotal := int64(0)
 | 
			
		||||
// 	jsonTotal := int64(0)
 | 
			
		||||
func TestBench(t *testing.T) {
 | 
			
		||||
	obj := testObj{
 | 
			
		||||
		String:  "hello",
 | 
			
		||||
		NoTag:   "no_tag",
 | 
			
		||||
		Int:     math.MaxInt,
 | 
			
		||||
		Int8:    math.MaxInt8,
 | 
			
		||||
		Int16:   math.MaxInt16,
 | 
			
		||||
		Int32:   math.MaxInt32,
 | 
			
		||||
		Int64:   math.MaxInt64,
 | 
			
		||||
		Uint:    math.MaxUint,
 | 
			
		||||
		Uint8:   math.MaxUint8,
 | 
			
		||||
		Uint16:  math.MaxUint16,
 | 
			
		||||
		Uint32:  math.MaxUint32,
 | 
			
		||||
		Uint64:  math.MaxUint64,
 | 
			
		||||
		Float32: math.MaxFloat32,
 | 
			
		||||
		Float64: math.MaxFloat64,
 | 
			
		||||
		// Complex64:  complex(math.MaxFloat32, math.MaxFloat32),
 | 
			
		||||
		// Complex128: complex(math.MaxFloat64, math.MaxFloat64),
 | 
			
		||||
	}
 | 
			
		||||
	that := require.New(t)
 | 
			
		||||
	asldTotal := int64(0)
 | 
			
		||||
	jsonTotal := int64(0)
 | 
			
		||||
 | 
			
		||||
// 	jsonMax := int64(0)
 | 
			
		||||
// 	jsonMin := math.MaxInt64
 | 
			
		||||
	jsonMax := int64(0)
 | 
			
		||||
	jsonMin := math.MaxInt64
 | 
			
		||||
 | 
			
		||||
// 	asldMax := int64(0)
 | 
			
		||||
// 	asldMin := math.MaxInt64
 | 
			
		||||
	asldMax := int64(0)
 | 
			
		||||
	asldMin := math.MaxInt64
 | 
			
		||||
 | 
			
		||||
// 	count := int64(1 << 20)
 | 
			
		||||
// 	for index := int64(0); index < count; index++ {
 | 
			
		||||
// 		objJSON, err := json.Marshal(obj)
 | 
			
		||||
// 		that.NoError(err)
 | 
			
		||||
	count := int64(1 << 20)
 | 
			
		||||
	for index := int64(0); index < count; index++ {
 | 
			
		||||
		objJSON, err := json.Marshal(obj)
 | 
			
		||||
		that.NoError(err)
 | 
			
		||||
 | 
			
		||||
// 		asldStart := time.Now()
 | 
			
		||||
// 		_, err = asld.Unmarshal[testObj](objJSON)
 | 
			
		||||
// 		asldDur := time.Since(asldStart)
 | 
			
		||||
// 		asldTotal += int64(asldDur)
 | 
			
		||||
// 		if asldDur < time.Duration(asldMin) {
 | 
			
		||||
// 			asldMin = int(asldDur)
 | 
			
		||||
// 		}
 | 
			
		||||
// 		if asldDur > time.Duration(asldMax) {
 | 
			
		||||
// 			asldMax = int64(asldDur)
 | 
			
		||||
// 		}
 | 
			
		||||
		asldStart := time.Now()
 | 
			
		||||
		_, err = ld.Unmarshal[testObj](objJSON)
 | 
			
		||||
		asldDur := time.Since(asldStart)
 | 
			
		||||
		asldTotal += int64(asldDur)
 | 
			
		||||
		if asldDur < time.Duration(asldMin) {
 | 
			
		||||
			asldMin = int(asldDur)
 | 
			
		||||
		}
 | 
			
		||||
		if asldDur > time.Duration(asldMax) {
 | 
			
		||||
			asldMax = int64(asldDur)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
// 		that.NoError(err)
 | 
			
		||||
// 		a := testObj{}
 | 
			
		||||
		that.NoError(err)
 | 
			
		||||
		a := testObj{}
 | 
			
		||||
 | 
			
		||||
// 		jsonStart := time.Now()
 | 
			
		||||
// 		err = json.Unmarshal(objJSON, &a)
 | 
			
		||||
// 		jsonDur := time.Since(jsonStart)
 | 
			
		||||
// 		jsonTotal += int64(jsonDur)
 | 
			
		||||
		jsonStart := time.Now()
 | 
			
		||||
		err = json.Unmarshal(objJSON, &a)
 | 
			
		||||
		jsonDur := time.Since(jsonStart)
 | 
			
		||||
		jsonTotal += int64(jsonDur)
 | 
			
		||||
 | 
			
		||||
// 		if jsonDur < time.Duration(jsonMin) {
 | 
			
		||||
// 			jsonMin = int(jsonDur)
 | 
			
		||||
// 		}
 | 
			
		||||
// 		if jsonDur > time.Duration(jsonMax) {
 | 
			
		||||
// 			jsonMax = int64(jsonDur)
 | 
			
		||||
// 		}
 | 
			
		||||
		if jsonDur < time.Duration(jsonMin) {
 | 
			
		||||
			jsonMin = int(jsonDur)
 | 
			
		||||
		}
 | 
			
		||||
		if jsonDur > time.Duration(jsonMax) {
 | 
			
		||||
			jsonMax = int64(jsonDur)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
// 		that.NoError(err)
 | 
			
		||||
// 	}
 | 
			
		||||
// 	fmt.Println(count, "runs")
 | 
			
		||||
// 	fmt.Printf("json avg (%s), min (%s), max (%s)\n", time.Duration(jsonTotal/count), time.Duration(jsonMin), time.Duration(jsonMax))
 | 
			
		||||
// 	fmt.Printf("asld avg (%s), min (%s), max (%s)\n", time.Duration(asldTotal/count), time.Duration(asldMin), time.Duration(asldMax))
 | 
			
		||||
// }
 | 
			
		||||
// func jsonElemList[T any](that *require.Assertions, a any) [][]byte {
 | 
			
		||||
// 	that.NotNil(a)
 | 
			
		||||
// 	v, ok := a.([]T)
 | 
			
		||||
// 	that.True(ok)
 | 
			
		||||
// 	fields := make([][]byte, len(v))
 | 
			
		||||
// 	for index, field := range v {
 | 
			
		||||
// 		jsonField, err := json.Marshal(field)
 | 
			
		||||
// 		that.NoError(err)
 | 
			
		||||
// 		fields[index] = jsonField
 | 
			
		||||
// 	}
 | 
			
		||||
// 	return fields
 | 
			
		||||
// }
 | 
			
		||||
		that.NoError(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Println(count, "runs")
 | 
			
		||||
	fmt.Printf("json avg (%s), min (%s), max (%s)\n", time.Duration(jsonTotal/count), time.Duration(jsonMin), time.Duration(jsonMax))
 | 
			
		||||
	fmt.Printf("asld avg (%s), min (%s), max (%s)\n", time.Duration(asldTotal/count), time.Duration(asldMin), time.Duration(asldMax))
 | 
			
		||||
}
 | 
			
		||||
func jsonElemList[T any](that *require.Assertions, a any) [][]byte {
 | 
			
		||||
	that.NotNil(a)
 | 
			
		||||
	v, ok := a.([]T)
 | 
			
		||||
	that.True(ok)
 | 
			
		||||
	fields := make([][]byte, len(v))
 | 
			
		||||
	for index, field := range v {
 | 
			
		||||
		jsonField, err := json.Marshal(field)
 | 
			
		||||
		that.NoError(err)
 | 
			
		||||
		fields[index] = jsonField
 | 
			
		||||
	}
 | 
			
		||||
	return fields
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// type hello struct {
 | 
			
		||||
// 	Hello string
 | 
			
		||||
// }
 | 
			
		||||
type hello struct {
 | 
			
		||||
	Hello string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func TestArrayMembers(t *testing.T) {
 | 
			
		||||
// 	tests := map[string]struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue