]> Cypherpunks repositories - keks.git/commitdiff
Tests fixes, refactoring, additions
authorAnton Rudenko <rudenko.ad@phystech.edu>
Fri, 31 Jan 2025 07:51:13 +0000 (10:51 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 10 Feb 2025 15:53:18 +0000 (18:53 +0300)
21 files changed:
go/atom-decode.go
go/atom-encode.go
go/bin_test.go
go/blob_test.go
go/bool_test.go
go/float_test.go
go/generic_test.go
go/hex_test.go [new file with mode: 0644]
go/int_test.go
go/junk_test.go [moved from go/support_test.go with 59% similarity]
go/list_test.go
go/magic_test.go [new file with mode: 0644]
go/map_test.go
go/parse.go
go/str_test.go [new file with mode: 0644]
go/string_test.go [deleted file]
go/tai_test.go
go/uuid_test.go
py3/tests/test_int.py
py3/tests/test_map.py
py3/tests/test_str.py

index 0cbcc53828c829c7fc8e8fbc80a93eb774b694170e4948efb2e2cf01274d61eb..6129fec2e30a86218fc9e5671268a14f1ab1e97b76d5d7005a082fce946baf1c 100644 (file)
@@ -35,8 +35,8 @@ var (
        ErrBadUTF8       = errors.New("invalid UTF-8")
        ErrBadInt        = errors.New("bad int value")
        ErrBadMagic      = errors.New("bad magic value")
-       ErrTaiReserved   = errors.New("reserved TAI64 values in use")
-       ErrTaiNonMinimal = errors.New("non-minimal TAI64")
+       ErrTAIReserved   = errors.New("reserved TAI64 values in use")
+       ErrTAINonMinimal = errors.New("non-minimal TAI64")
        ErrTooManyNsecs  = errors.New("too many nanoseconds")
        ErrTooManyAsecs  = errors.New("too many attoseconds")
 )
@@ -212,13 +212,13 @@ func (ctx *Decoder) DecodeAtom() (t types.Type, err error) {
                        return
                }
                if be.Get([]byte(s)[:8]) > (1 << 63) {
-                       err = ErrTaiReserved
+                       err = ErrTAIReserved
                        return
                }
                if l > 8 {
                        nsecs := be.Get([]byte(s)[8 : 8+4])
                        if l == 12 && nsecs == 0 {
-                               err = ErrTaiNonMinimal
+                               err = ErrTAINonMinimal
                                return
                        }
                        if nsecs > 999999999 {
@@ -229,7 +229,7 @@ func (ctx *Decoder) DecodeAtom() (t types.Type, err error) {
                if l > 12 {
                        asecs := be.Get([]byte(s)[8+4 : 8+4+4])
                        if asecs == 0 {
-                               err = ErrTaiNonMinimal
+                               err = ErrTAINonMinimal
                                return
                        }
                        if asecs > 999999999 {
index 88d818cdb98b5c368ea33a39584260cb056a9fe89ccdef64fb575085dd5af944..25a013964beab56aa5d6cc969180be62e1cc39d9f7c6e73820f0e5f43b666bdb 100644 (file)
@@ -129,6 +129,9 @@ func BigIntEncode(w io.Writer, v *big.Int) (written int64, err error) {
 
 // Write an encoded BLOB atom.
 func BlobEncode(w io.Writer, chunkLen int64, r io.Reader) (written int64, err error) {
+       if chunkLen == 0 {
+               return 0, errors.New("zero chunkLen specified")
+       }
        {
                l := make([]byte, 9)
                l[0] = byte(AtomBLOB)
index 31a455fbb9dc3a77fd9821a7eed19dde009ab1934c826a88b503235ca455c257..e04a6ea579c4526e515964b03214adbb34f00b801407d0089838fc808f1ba506 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,206 +18,261 @@ package keks
 
 import (
        "bytes"
-       "encoding/hex"
        "io"
-       "reflect"
        "strings"
        "testing"
        "testing/quick"
+
+       "go.cypherpunks.su/keks/be"
 )
 
-func TestBinaryEmpty(t *testing.T) {
-       object := []byte{}
-       binstring := mustHexDec("80")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinEmpty(t *testing.T) {
+       obj := []byte{}
+       bin := mustHexDec("80")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.([]byte)
+       casted, ok := decoded.([]byte)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
+       }
+       if !bytes.Equal(casted, obj) {
+               t.Fatal("casted differs")
        }
-       if !bytes.Equal(udecoded, object) {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestBinary1234(t *testing.T) {
-       object := []byte{0x01, 0x02, 0x03, 0x04}
-       binstring := mustHexDec("8401020304")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBin1234(t *testing.T) {
+       obj := []byte{0x01, 0x02, 0x03, 0x04}
+       bin := mustHexDec("8401020304")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.([]byte)
+       casted, ok := decoded.([]byte)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
+       }
+       if !bytes.Equal(casted, obj) {
+               t.Fatal("casted differs")
        }
-       if !bytes.Equal(udecoded, object) {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(udecoded), udecoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestBinaryLen62(t *testing.T) {
-       object := bytes.Repeat([]byte{'a'}, 62)
-       binstring := mustHexDec("BD01" + strings.Repeat("61", 62))
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinLen62(t *testing.T) {
+       obj := bytes.Repeat([]byte{'a'}, 62)
+       bin := mustHexDec("BD01" + strings.Repeat("61", 62))
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.([]byte)
+       casted, ok := decoded.([]byte)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
        }
-       if !bytes.Equal(udecoded, object) {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(udecoded), udecoded)
-               t.FailNow()
+       if !bytes.Equal(casted, obj) {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestBinaryLen318(t *testing.T) {
-       object := bytes.Repeat([]byte{'a'}, 318)
-       binstring := mustHexDec("BE0001" + strings.Repeat("61", 318))
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinLen318(t *testing.T) {
+       obj := bytes.Repeat([]byte{'a'}, 62+255+1)
+       bin := mustHexDec("BE0001" + strings.Repeat("61", 62+255+1))
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.([]byte)
+       casted, ok := decoded.([]byte)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
+       }
+       if !bytes.Equal(casted, obj) {
+               t.Fatal("casted differs")
        }
-       if !bytes.Equal(udecoded, object) {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(udecoded), udecoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestBinaryLen65853(t *testing.T) {
-       object := bytes.Repeat([]byte{'a'}, 65853)
-       binstring := mustHexDec("BF0000000000000000" + strings.Repeat("61", 65853))
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinLen65853(t *testing.T) {
+       obj := bytes.Repeat([]byte{'a'}, 62+255+65535+1)
+       bin := mustHexDec("BF0000000000000000" + strings.Repeat("61", 62+255+65535+1))
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.([]byte)
+       casted, ok := decoded.([]byte)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
+       }
+       if !bytes.Equal(casted, obj) {
+               t.Fatal("casted differs")
        }
-       if !bytes.Equal(udecoded, object) {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(udecoded), udecoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestBinaryThrowsWhenNotEnoughData(t *testing.T) {
-       binstring := mustHexDec("84010203")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinNotEnoughData(t *testing.T) {
+       bin := mustHexDec("84010203")
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
+               t.Fatal()
        }
 }
 
-func TestBinaryThrowsWhenNotEnoughDataForLength8(t *testing.T) {
-       binstring := mustHexDec("BD01" + strings.Repeat("61", 61))
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinNotEnoughDataForLength8(t *testing.T) {
+       bin := mustHexDec("BD01" + strings.Repeat("61", 61))
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
+               t.Fatal()
        }
 }
 
-func TestBinaryThrowsWhenNotEnoughDataForLength16(t *testing.T) {
-       binstring := mustHexDec("BE0001" + strings.Repeat("61", 317))
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinNotEnoughDataForLength16(t *testing.T) {
+       bin := mustHexDec("BE0001" + strings.Repeat("61", 317))
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
+               t.Fatal()
        }
 }
 
-func TestBinaryThrowsWhenNotEnoughDataForLength64(t *testing.T) {
-       binstring := mustHexDec("BF0000000000000000" + strings.Repeat("61", 65852))
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBinNotEnoughDataForLength64(t *testing.T) {
+       bin := mustHexDec("BF0000000000000000" + strings.Repeat("61", 65852))
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
+               t.Fatal()
+       }
+}
+
+func TestBinTooLong(t *testing.T) {
+       bin := make([]byte, 9)
+       bin[0] = 0xBF
+       for _, l := range []uint64{
+               1 << 63,
+               (1 << 63) - 63,
+               (1 << 63) - 63 - ((1 << 8) - 1),
+               (1 << 63) - 63 - ((1 << 8) - 1) - ((1 << 16) - 1) + 1,
+       } {
+               be.Put(bin[1:], l)
+               decoder := NewDecoderFromBytes(bin, nil)
+               _, err := decoder.Decode()
+               if err != ErrLenTooBig {
+                       t.Fatal(err)
+               }
        }
 }
 
-func TestBinarySymmetric(t *testing.T) {
-       f := func(object []byte) bool {
-               encoded, err := EncodeBuf(object, nil)
+func TestBinNotTooLong(t *testing.T) {
+       bin := make([]byte, 9)
+       bin[0] = 0xBF
+       be.Put(bin[1:], (1<<63)-63-((1<<8)-1)-((1<<16)-1))
+       decoder := NewDecoderFromReader(bytes.NewReader(bin), nil)
+       defer func() { recover() }()
+       _, err := decoder.Decode()
+       // it won't reach that code because it can not allocate so much
+       // memory in .getBytes(), but that means, that no error was returned
+       t.Fatal(err)
+}
+
+func TestBinMaxStrLen(t *testing.T) {
+       f := func(bin []byte) bool {
+               if len(bin) == 0 {
+                       return true
+               }
+               encoded, err := EncodeBuf(bin, nil)
+               if err != nil {
+                       panic(err)
+               }
+               decoder := NewDecoderFromBytes(encoded,
+                       &DecodeOpts{MaxStrLen: int64(len(bin) + 1)})
+               _, err = decoder.Decode()
                if err != nil {
                        return false
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder = NewDecoderFromBytes(encoded,
+                       &DecodeOpts{MaxStrLen: int64(len(bin))})
+               _, err = decoder.Decode()
                if err != nil {
                        return false
                }
-               udecoded, ok := decoded.([]byte)
-               if !ok {
-                       t.Fatalf("failed to cast")
+               if len(bin) == 1 {
+                       return true
                }
-               if !bytes.Equal(object, udecoded) {
-                       t.Logf("Expected <%s, %d>", reflect.TypeOf(object), object)
-                       t.Logf("Instead  <%s, %d>", reflect.TypeOf(udecoded), udecoded)
+               decoder = NewDecoderFromBytes(encoded,
+                       &DecodeOpts{MaxStrLen: int64(len(bin) - 1)})
+               _, err = decoder.Decode()
+               return err != nil
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestBinSymmetric(t *testing.T) {
+       f := func(bin []byte) bool {
+               encoded, err := EncodeBuf(bin, nil)
+               if err != nil {
+                       panic(err)
+               }
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
+               if err != nil {
+                       return false
+               }
+               casted, ok := decoded.([]byte)
+               if !ok {
                        return false
                }
-               return true
+               return bytes.Equal(bin, casted)
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
index a3422b086bc743a21ac05f83a6c2379c56ad8f3ec5b9a5adc5e4f3057e3e00d0..a97d5cb03c55c893fa0b93cc985f6b2d98b1b633d57bfdd1754e5db93ad91f0a 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,147 +18,218 @@ package keks
 
 import (
        "bytes"
-       "encoding/hex"
        "io"
-       "reflect"
        "testing"
        "testing/quick"
+
+       "go.cypherpunks.su/keks/be"
 )
 
 func TestBlobMultipleOfChunkLen(t *testing.T) {
-       object := BlobChunked{
-               Chunks:   []string{"test", "data"},
+       bin := bytes.Join([][]byte{
+               mustHexDec("0B0000000000000003"),
+               {0x84},
+               []byte("test"),
+               {0x84},
+               []byte("data"),
+               {0x80},
+       }, nil)
+       data := []byte("test" + "data")
+       encoded, err := EncodeBuf(BlobReader{
                ChunkLen: 4,
-       }
-       binstring := mustHexDec("0B00000000000000038474657374846461746180")
-       encoded, err := EncodeBuf(object, nil)
+               R:        bytes.NewReader(data),
+       }, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+       decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(BlobChunked)
+       casted, ok := decoded.(BlobChunked)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
        }
-       if object.ChunkLen != udecoded.ChunkLen {
-               t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-               t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
+       if casted.Len() != 2*4 {
+               t.Fatal("Len() differs")
        }
-       for i := range len(object.Chunks) {
-               if object.Chunks[i] != udecoded.Chunks[i] {
-                       t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-                       t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
-               }
+       if casted.ChunkLen != 4 {
+               t.Fatal("ChunkLen differs")
+       }
+       if len(casted.Chunks) != 2 {
+               t.Fatal("len(Chunks) differs")
+       }
+       if casted.Chunks[0] != "test" {
+               t.Fatal("Chunks[0] differs")
+       }
+       if casted.Chunks[1] != "data" {
+               t.Fatal("Chunks[1] differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+       got, err := io.ReadAll(casted.Reader())
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(got, data) {
+               t.Fatal("reader differs")
        }
 }
 
 func TestBlobLargerOfChunkLen(t *testing.T) {
-       object := BlobChunked{
-               Chunks:   []string{"test", "data", "2"},
+       bin := bytes.Join([][]byte{
+               mustHexDec("0B0000000000000003"),
+               {0x84},
+               []byte("test"),
+               {0x84},
+               []byte("data"),
+               {0x81},
+               []byte("2"),
+       }, nil)
+       data := []byte("test" + "data" + "2")
+       encoded, err := EncodeBuf(BlobReader{
                ChunkLen: 4,
-       }
-       binstring := mustHexDec("0B0000000000000003847465737484646174618132")
-       encoded, err := EncodeBuf(object, nil)
+               R:        bytes.NewReader(data),
+       }, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+       decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(BlobChunked)
+       casted, ok := decoded.(BlobChunked)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
        }
-       if object.ChunkLen != udecoded.ChunkLen {
-               t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-               t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
+       if casted.Len() != 2*4+1 {
+               t.Fatal("Len() differs")
        }
-       for i := range len(object.Chunks) {
-               if object.Chunks[i] != udecoded.Chunks[i] {
-                       t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-                       t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
-               }
+       if casted.ChunkLen != 4 {
+               t.Fatal("ChunkLen differs")
+       }
+       if len(casted.Chunks) != 3 {
+               t.Fatal("len(Chunks) differs")
+       }
+       if casted.Chunks[0] != "test" {
+               t.Fatal("Chunks[0] differs")
+       }
+       if casted.Chunks[1] != "data" {
+               t.Fatal("Chunks[1] differs")
+       }
+       if casted.Chunks[2] != "2" {
+               t.Fatal("Chunks[2] differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+       got, err := io.ReadAll(casted.Reader())
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(got, data) {
+               t.Fatal("reader differs")
        }
 }
 
 func TestBlobEmpty(t *testing.T) {
-       object := BlobChunked{
-               Chunks:   []string{},
+       bin := mustHexDec("0B0000000000000003" + "80")
+       encoded, err := EncodeBuf(BlobReader{
                ChunkLen: 4,
-       }
-       binstring := mustHexDec("0B000000000000000380")
-       encoded, err := EncodeBuf(object, nil)
+               R:        bytes.NewReader(nil),
+       }, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+       decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(BlobChunked)
+       casted, ok := decoded.(BlobChunked)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
        }
-       if object.ChunkLen != udecoded.ChunkLen {
-               t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-               t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
+       if casted.Len() != 0 {
+               t.Fatal("Len() differs")
        }
-       for i := range len(object.Chunks) {
-               if object.Chunks[i] != udecoded.Chunks[i] {
-                       t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-                       t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
-               }
+       if casted.ChunkLen != 4 {
+               t.Fatal("ChunkLen differs")
+       }
+       if len(casted.Chunks) != 0 {
+               t.Fatal("len(Chunks) differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+       got, err := io.ReadAll(casted.Reader())
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(got, []byte{}) {
+               t.Fatal("reader differs")
        }
 }
 
 func TestBlobSymmetric(t *testing.T) {
-       f := func(data []byte, ChunkLen uint8) bool {
-               object := BlobReader{ChunkLen: int64(ChunkLen), R: bytes.NewReader(data)}
-               encoded, err := EncodeBuf(object, nil)
+       var err error
+       f := func(chunkLen, chunksN uint8) bool {
+               if chunkLen == 0 {
+                       chunkLen++
+               }
+               chunks := make([][]byte, 0, chunksN)
+               mr := make([]io.Reader, 0, chunksN)
+               for range chunksN {
+                       chunk := make([]byte, chunkLen)
+                       if _, err = io.ReadFull(PRNG, chunk); err != nil {
+                               panic(err)
+                       }
+                       chunks = append(chunks, chunk)
+                       mr = append(mr, bytes.NewReader(chunk))
+               }
+               encoded, err := EncodeBuf(BlobReader{
+                       ChunkLen: int64(chunkLen),
+                       R:        io.MultiReader(mr...),
+               }, nil)
                if err != nil {
-                       t.Fatalf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Fatalf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
+               }
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               udecoded, ok := decoded.(BlobChunked)
+               casted, ok := decoded.(BlobChunked)
                if !ok {
-                       t.Fatalf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if object.ChunkLen != udecoded.ChunkLen {
-                       t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-                       t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
-                       return false
+               if casted.Len() != int64(chunkLen)*int64(chunksN) {
+                       t.Fatal("Len() differs")
                }
-               for i := range len(data) {
-                       if data[i] != udecoded.Chunks[i/int(ChunkLen)][i%int(ChunkLen)] {
-                               t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-                               t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
-                               return false
-                       }
+               if casted.ChunkLen != int64(chunkLen) {
+                       t.Fatal("ChunkLen differs")
                }
-               for i := range int(ChunkLen)*(len(data)/int(ChunkLen)) - len(data) {
-                       if udecoded.Chunks[len(data)/int(ChunkLen)][i] != 0 {
-                               t.Fatalf("expected <%s, %v>", reflect.TypeOf(object), object)
-                               t.Fatalf("instead  <%s, %v>", reflect.TypeOf(decoded), decoded)
-                               return false
+               if len(casted.Chunks) != int(chunksN) {
+                       t.Fatal("len(Chunks) differs")
+               }
+               for i := range len(chunks) {
+                       if casted.Chunks[i] != string(chunks[i]) {
+                               t.Fatal("chunk differs")
                        }
                }
                return true
@@ -167,34 +239,66 @@ func TestBlobSymmetric(t *testing.T) {
        }
 }
 
-func TestBlobThrowsWhenNotEnoughData(t *testing.T) {
-       binstring := mustHexDec("0B00000000000000038474657374846461")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBlobNotEnoughData(t *testing.T) {
+       bin := bytes.Join([][]byte{
+               mustHexDec("0B0000000000000003"),
+               {0x84},
+               []byte("test"),
+               {0x84},
+               []byte("da"),
+       }, nil)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %s", err)
+               t.Fatal(err)
+       }
+}
+
+func TestBlobTooLong(t *testing.T) {
+       bin := make([]byte, 1+8)
+       bin[0] = byte(AtomBLOB)
+       be.Put(bin[1:], (1<<63)-1)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
+       if err != ErrLenTooBig {
+               t.Fatal(err)
        }
 }
 
-func TestBlobThrowsWhenNotEnoughDataForLength(t *testing.T) {
-       binstring := mustHexDec("0B00000000")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBlobNotEnoughDataForLength(t *testing.T) {
+       bin := mustHexDec("0B00000000")
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %s", err)
+               t.Fatal(err)
        }
 }
 
-func TestBlobThrowsWhenWrongTerminatorLength(t *testing.T) {
-       binstring := mustHexDec("0B0000000000000003847465737484646174618A7465726D696E61746F72")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBlobWrongTerminatorLength(t *testing.T) {
+       bin := bytes.Join([][]byte{
+               mustHexDec("0B0000000000000003"),
+               {0x84},
+               []byte("test"),
+               {0x84},
+               []byte("data"),
+               {0x8A},
+               []byte("terminator"),
+       }, nil)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != ErrBlobBadChunkLen {
-               t.Fatalf("expected ErrBlobBadChunkLen, got %s", err)
+               t.Fatal(err)
        }
 }
 
-func TestBlobThrowsWhenWrongTerminatorTag(t *testing.T) {
-       binstring := mustHexDec("0B00000000000000038474657374846461746104746861742077617320612077726F6E6720746167")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBlobWrongTerminatorTag(t *testing.T) {
+       bin := bytes.Join([][]byte{
+               mustHexDec("0B0000000000000003"),
+               {0x84},
+               []byte("test"),
+               {0x84},
+               []byte("data"),
+               {0x04},
+               []byte("that was a wrong tag"),
+       }, nil)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != ErrBlobBadAtom {
-               t.Fatalf("expected ErrBlobBadChunkLen, got %s", err)
+               t.Fatal(err)
        }
 }
index eaed60193fde99ebc1e64838660278f202e0af9a0cb076392e5e55e90367408d..c5c1a02588d5344362ed0858528bc23ae2175019828b9a942f93455d179f3e43 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,49 +18,57 @@ package keks
 
 import (
        "bytes"
-       "encoding/hex"
-       "reflect"
        "testing"
 )
 
-func TestTrue(t *testing.T) {
-       object := true
-       binstring := mustHexDec("03")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBoolTrue(t *testing.T) {
+       bin := mustHexDec("03")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(true, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if decoded != object {
-               t.Logf("expected type %s, value true", reflect.TypeOf(object))
-               t.Logf("got type %s, value %t", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       casted, ok := decoded.(bool)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if !casted {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestFalse(t *testing.T) {
-       object := false
-       binstring := mustHexDec("02")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestBoolFalse(t *testing.T) {
+       bin := mustHexDec("02")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(false, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(bool)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if casted {
+               t.Fatal("casted differs")
        }
-       if decoded != object {
-               t.Logf("expected type %s, value false", reflect.TypeOf(object))
-               t.Logf("got type %s, value %+v", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
index 5c066023e3293f1a719c1fa5b4613243f6f5acdf18cac8cb919021a6e95d55e3..8e32f82c3ac8faabfd552f15f42a82a1c8cbc0f6e4880488071aa62573f32978 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -18,123 +19,147 @@ package keks
 import (
        "bytes"
        "io"
-       "reflect"
        "testing"
 )
 
-func TestThrowsWhenEncodingFloat(t *testing.T) {
+func TestEncodingFloat(t *testing.T) {
        _, err := EncodeBuf(1.5, nil)
        if err == nil {
-               t.Fatalf("expected Error, got nil")
+               t.Fatal(err)
        }
 }
 
 func TestFloat16Loads(t *testing.T) {
-       decoded, err := NewDecoderFromBytes(append([]byte{0x10}, bytes.Repeat([]byte{0x11}, 2)...), nil).Decode()
-       object := Raw(append([]byte{0x10}, bytes.Repeat([]byte{0x11}, 2)...))
+       bin := append([]byte{0x10}, bytes.Repeat([]byte{0x11}, 2)...)
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %v", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(Raw)
+       casted, ok := decoded.(Raw)
        if !ok {
-               t.Fatalf("expected Raw, got %v", reflect.TypeOf(decoded))
+               t.Fatal("failed to cast")
        }
-       if !bytes.Equal(object, udecoded) {
-               t.Fatalf("expected %v, got %v", object, decoded)
+       if !bytes.Equal(casted, bin) {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
 func TestFloat32Loads(t *testing.T) {
-       decoded, err := NewDecoderFromBytes(append([]byte{0x11}, bytes.Repeat([]byte{0x11}, 4)...), nil).Decode()
-       object := Raw(append([]byte{0x11}, bytes.Repeat([]byte{0x11}, 4)...))
+       bin := append([]byte{0x11}, bytes.Repeat([]byte{0x11}, 4)...)
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %v", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(Raw)
+       casted, ok := decoded.(Raw)
        if !ok {
-               t.Fatalf("expected Raw, got %v", reflect.TypeOf(decoded))
+               t.Fatal("failed to cast")
+       }
+       if !bytes.Equal(casted, bin) {
+               t.Fatal("casted differs")
        }
-       if !bytes.Equal(object, udecoded) {
-               t.Fatalf("expected %v, got %v", object, decoded)
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
 func TestFloat64Loads(t *testing.T) {
-       decoded, err := NewDecoderFromBytes(append([]byte{0x12}, bytes.Repeat([]byte{0x11}, 8)...), nil).Decode()
-       object := Raw(append([]byte{0x12}, bytes.Repeat([]byte{0x11}, 8)...))
+       bin := append([]byte{0x12}, bytes.Repeat([]byte{0x12}, 8)...)
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %v", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(Raw)
+       casted, ok := decoded.(Raw)
        if !ok {
-               t.Fatalf("expected Raw, got %v", reflect.TypeOf(decoded))
+               t.Fatal("failed to cast")
+       }
+       if !bytes.Equal(casted, bin) {
+               t.Fatal("casted differs")
        }
-       if !bytes.Equal(object, udecoded) {
-               t.Fatalf("expected %v, got %v", object, decoded)
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
 func TestFloat128Loads(t *testing.T) {
-       decoded, err := NewDecoderFromBytes(append([]byte{0x13}, bytes.Repeat([]byte{0x11}, 16)...), nil).Decode()
-       object := Raw(append([]byte{0x13}, bytes.Repeat([]byte{0x11}, 16)...))
+       bin := append([]byte{0x13}, bytes.Repeat([]byte{0x13}, 16)...)
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %v", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(Raw)
+       casted, ok := decoded.(Raw)
        if !ok {
-               t.Fatalf("expected Raw, got %v", reflect.TypeOf(decoded))
+               t.Fatal("failed to cast")
        }
-       if !bytes.Equal(object, udecoded) {
-               t.Fatalf("expected %v, got %v", object, decoded)
+       if !bytes.Equal(casted, bin) {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
 func TestFloat256Loads(t *testing.T) {
-       decoded, err := NewDecoderFromBytes(append([]byte{0x14}, bytes.Repeat([]byte{0x11}, 32)...), nil).Decode()
-       object := Raw(append([]byte{0x14}, bytes.Repeat([]byte{0x11}, 32)...))
+       bin := append([]byte{0x14}, bytes.Repeat([]byte{0x14}, 32)...)
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %v", err)
+               t.Fatal(err)
        }
-       udecoded, ok := decoded.(Raw)
+       casted, ok := decoded.(Raw)
        if !ok {
-               t.Fatalf("expected Raw, got %v", reflect.TypeOf(decoded))
+               t.Fatal("failed to cast")
+       }
+       if !bytes.Equal(casted, bin) {
+               t.Fatal("casted differs")
        }
-       if !bytes.Equal(object, udecoded) {
-               t.Fatalf("expected %v, got %v", object, decoded)
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestFloatThrowhsWhenNotEnoughDataForFloat256(t *testing.T) {
-       _, err := NewDecoderFromBytes(append([]byte{0x14}, bytes.Repeat([]byte{0x11}, 32-1)...), nil).Decode()
+func TestFloat16NotEnoughData(t *testing.T) {
+       bin := append([]byte{0x10}, bytes.Repeat([]byte{0x11}, 2-1)...)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %s", err)
+               t.Fatal(err)
        }
 }
 
-func TestFloatThrowhsWhenNotEnoughDataForFloat128(t *testing.T) {
-       _, err := NewDecoderFromBytes(append([]byte{0x13}, bytes.Repeat([]byte{0x11}, 16-1)...), nil).Decode()
+func TestFloat32NotEnoughData(t *testing.T) {
+       bin := append([]byte{0x11}, bytes.Repeat([]byte{0x11}, 4-1)...)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %s", err)
+               t.Fatal(err)
        }
 }
 
-func TestFloatThrowhsWhenNotEnoughDataForFloat64(t *testing.T) {
-       _, err := NewDecoderFromBytes(append([]byte{0x12}, bytes.Repeat([]byte{0x11}, 8-1)...), nil).Decode()
+func TestFloat64NotEnoughData(t *testing.T) {
+       bin := append([]byte{0x12}, bytes.Repeat([]byte{0x11}, 8-1)...)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %s", err)
+               t.Fatal(err)
        }
 }
 
-func TestFloatThrowhsWhenNotEnoughDataForFloat32(t *testing.T) {
-       _, err := NewDecoderFromBytes(append([]byte{0x11}, bytes.Repeat([]byte{0x11}, 4-1)...), nil).Decode()
+func TestFloat128NotEnoughData(t *testing.T) {
+       bin := append([]byte{0x13}, bytes.Repeat([]byte{0x11}, 16-1)...)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %s", err)
+               t.Fatal(err)
        }
 }
 
-func TestFloatThrowhsWhenNotEnoughDataForFloat16(t *testing.T) {
-       _, err := NewDecoderFromBytes(append([]byte{0x10}, bytes.Repeat([]byte{0x11}, 2-1)...), nil).Decode()
+func TestFloat256NotEnoughData(t *testing.T) {
+       bin := append([]byte{0x14}, bytes.Repeat([]byte{0x11}, 32-1)...)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %s", err)
+               t.Fatal(err)
        }
 }
index e7ad006f8dffe3a1c9017b0b7751ccbb89975f92fb5b01acaeb22e3889ab8c02..738b00958b958ee806e2dc3c762999da9e9bcc2e159d2066dedf0f27219c0309 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -20,26 +21,24 @@ import (
        "testing"
 )
 
-func TestThrowsWhenUnknownTag(t *testing.T) {
-       binstring := []byte{0x05}
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestUnknownTag(t *testing.T) {
+       _, err := NewDecoderFromBytes(append([]byte{0x05}, Junk...), nil).Decode()
        if err != ErrUnknownType {
-               t.Fatalf("expected ErrUnknownType, got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestThrowsWhenEmptyData(t *testing.T) {
+func TestEmptyData(t *testing.T) {
        binstring := []byte{}
        _, err := NewDecoderFromBytes(binstring, nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
+               t.Fatal(err)
        }
 }
 
 func TestLonelyEOC(t *testing.T) {
-       binstring := []byte{0x00}
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+       _, err := NewDecoderFromBytes(append([]byte{0x00}, Junk...), nil).Decode()
        if err != ErrUnexpectedEOC {
-               t.Fatalf("expected ErrUnexpectedEOC, got %v", err)
+               t.Fatal(err)
        }
 }
diff --git a/go/hex_test.go b/go/hex_test.go
new file mode 100644 (file)
index 0000000..fcedbe5
--- /dev/null
@@ -0,0 +1,13 @@
+package keks
+
+import (
+       "encoding/hex"
+)
+
+func mustHexDec(s string) []byte {
+       b, err := hex.DecodeString(s)
+       if err != nil {
+               panic(err)
+       }
+       return b
+}
index fbfc886c8121b433f104868bc095a5bfa1b84a77c76a766204a2acfa185ec45d..de40f88cf608fcc1ef068be6340a355b13fc2dc3893d7bd579e3ce2755badfec 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,683 +18,641 @@ package keks
 
 import (
        "bytes"
-       "encoding/hex"
        "io"
        "math/big"
-       "reflect"
        "testing"
        "testing/quick"
 )
 
-func TestIntZero(t *testing.T) {
-       object := 0
-       binstring := mustHexDec("0C80")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestInt0(t *testing.T) {
+       obj := int(0)
+       bin := mustHexDec("0C80")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(uint64)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if casted != uint64(obj) {
+               t.Fatal("casted differs")
        }
-       if decoded != uint64(object) {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestIntOne(t *testing.T) {
-       object := 1
-       binstring := mustHexDec("0C8101")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestInt1(t *testing.T) {
+       obj := int(1)
+       bin := mustHexDec("0C8101")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       casted, ok := decoded.(uint64)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != uint64(obj) {
+               t.Fatal("casted differs")
        }
-       if decoded != uint64(object) {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestIntNegativeOne(t *testing.T) {
-       object := -1
-       binstring := mustHexDec("0D80")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestIntM1(t *testing.T) {
+       obj := int(-1)
+       bin := mustHexDec("0D80")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if decoded != int64(object) {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       casted, ok := decoded.(int64)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != int64(obj) {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestInt1s63Minus1(t *testing.T) {
-       object := uint64(1<<63 - 1)
-       binstring := mustHexDec("0c887fffffffffffffff")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestInt123(t *testing.T) {
+       obj := uint(123)
+       bin := mustHexDec("0C817B")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       casted, ok := decoded.(uint64)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != uint64(obj) {
+               t.Fatal("casted differs")
        }
-       if decoded != object {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestInt1s63(t *testing.T) {
-       object, _ := big.NewInt(0).SetString("18446744073709551616", 10)
-       binstring := mustHexDec("0c89010000000000000000")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestIntM123(t *testing.T) {
+       obj := int(-123)
+       bin := mustHexDec("0D817A")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
-       t.Log(hex.EncodeToString(encoded))
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.(*big.Int)
+       casted, ok := decoded.(int64)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
        }
-       if udecoded.Cmp(object) != 0 {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if casted != int64(obj) {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestInt1s64(t *testing.T) {
-       object := big.NewInt(1).SetBytes(mustHexDec("010000000000000000"))
-       binstring := mustHexDec("0C89010000000000000000")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestIntMaxAllowable(t *testing.T) {
+       obj := uint64(1<<63) - 1
+       bin := mustHexDec("0C887FFFFFFFFFFFFFFF")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       casted, ok := decoded.(uint64)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != uint64(obj) {
+               t.Fatal("casted differs")
        }
-       if decoded.(*big.Int).Cmp(object) != 0 {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestInt1s130(t *testing.T) {
-       object := big.NewInt(1).SetBytes(mustHexDec("0400000000000000000000000000000000"))
-       binstring := mustHexDec("0C910400000000000000000000000000000000")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestIntNMaxAllowable(t *testing.T) {
+       obj := -int64(uint64(1<<63) - 1)
+       bin := mustHexDec("0D887FFFFFFFFFFFFFFE")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       casted, ok := decoded.(int64)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       if decoded.(*big.Int).Cmp(object) != 0 {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if casted != obj {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestIntM1s62Plus1(t *testing.T) {
-       object := int64(-9223372036854775808)
-       binstring := mustHexDec("0D887FFFFFFFFFFFFFFF")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestInt64bit(t *testing.T) {
+       obj := big.NewInt(0)
+       obj = obj.SetBit(obj, 64, 1)
+       bin := mustHexDec("0C89010000000000000000")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(*big.Int)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if casted.Cmp(obj) != 0 {
+               t.Fatal("casted differs")
        }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+}
+
+func TestIntM64bit(t *testing.T) {
+       obj := big.NewInt(0)
+       obj = obj.SetBit(obj, 64, 1)
+       obj = obj.Neg(obj)
+       bin := mustHexDec("0D88FFFFFFFFFFFFFFFF")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.(int64)
+       casted, ok := decoded.(*big.Int)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
        }
-       if udecoded != object {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if casted.Cmp(obj) != 0 {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestIntM1s62(t *testing.T) {
-       object, _ := big.NewInt(0).SetString("-9223372036854775809", 10)
-       binstring := mustHexDec("0D888000000000000000")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestIntM64bitAnd1(t *testing.T) {
+       obj := big.NewInt(0)
+       obj = obj.SetBit(obj, 64, 1)
+       obj = obj.SetBit(obj, 0, 1)
+       obj = obj.Neg(obj)
+       bin := mustHexDec("0D89010000000000000000")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       casted, ok := decoded.(*big.Int)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted.Cmp(obj) != 0 {
+               t.Fatal("casted differs")
        }
-       if decoded.(*big.Int).Cmp(object) != 0 {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestIntM1s64(t *testing.T) {
-       object := big.NewInt(0).SetBytes(mustHexDec("010000000000000000"))
-       object = object.Neg(object)
-       binstring := mustHexDec("0D88FFFFFFFFFFFFFFFF")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestInt131bit(t *testing.T) {
+       obj := big.NewInt(0)
+       obj = obj.SetBit(obj, 130, 1)
+       bin := mustHexDec("0C910400000000000000000000000000000000")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       casted, ok := decoded.(*big.Int)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       if decoded.(*big.Int).Cmp(object) != 0 {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if casted.Cmp(obj) != 0 {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestIntM1s130(t *testing.T) {
-       object := big.NewInt(1).SetBytes(mustHexDec("0100000000000000000000000000000000"))
-       object = object.Neg(object)
-       binstring := mustHexDec("0D90FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestIntM131bit(t *testing.T) {
+       obj := big.NewInt(0)
+       obj = obj.SetBit(obj, 130, 1)
+       obj = obj.Neg(obj)
+       bin := mustHexDec("0D9103FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+       casted, ok := decoded.(*big.Int)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       if decoded.(*big.Int).Cmp(object) != 0 {
-               t.Logf("expected type %s, value %d", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %d", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if casted.Cmp(obj) != 0 {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestIntThrowsWhenNotEnoughData(t *testing.T) {
+func TestIntNotEnoughData(t *testing.T) {
        _, err := NewDecoderFromBytes(mustHexDec("0C830102"), nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected error, got %v", err)
+               t.Fatal(err)
        }
        _, err = NewDecoderFromBytes(mustHexDec("0C8301"), nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected error, got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestIntThrowsWhenNonMinimalInt(t *testing.T) {
+func TestIntNonMinimalInt(t *testing.T) {
        encoded := mustHexDec("0C81007B")
        _, err := NewDecoderFromBytes(encoded, nil).Decode()
        if err != ErrIntNonMinimal {
-               t.Fatalf("expected ErrIntNonMinimal, got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestIntThrowsWhenNonBinInInt(t *testing.T) {
+func TestIntNonBinInInt(t *testing.T) {
        encoded := mustHexDec("0C017B")
        _, err := NewDecoderFromBytes(encoded, nil).Decode()
        if err != ErrBadInt {
-               t.Fatalf("expected ErrBadInt, got %v", err)
-       }
-}
-
-func TestInt64UnsignedSymmetric(t *testing.T) {
-       f := func(x uint64) bool {
-               encoded, err := EncodeBuf(x, nil)
-               if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
-               }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
-               if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
-               }
-               udecoded, ok := decoded.(uint64)
-               if !ok {
-                       t.Logf("failed to cast")
-                       return false
-               }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt32UnsignedSymmetric(t *testing.T) {
-       f := func(x uint32) bool {
+func TestInt64Symmetric(t *testing.T) {
+       f := func(x uint64) bool {
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(uint64)
+               casted, ok := decoded.(uint64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == x
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt16UnsignedSymmetric(t *testing.T) {
-       f := func(x uint16) bool {
-               encoded, err := EncodeBuf(x, nil)
-               if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
-               }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
-               if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
-               }
-               udecoded, ok := decoded.(uint64)
-               if !ok {
-                       t.Logf("failed to cast")
-                       return false
-               }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+func TestInt64NSymmetric(t *testing.T) {
+       f := func(x int64) bool {
+               if x >= 0 {
+                       return true
                }
-               return true
-       }
-       if err := quick.Check(f, nil); err != nil {
-               t.Fatal(err)
-       }
-}
-
-func TestInt8UnsignedSymmetric(t *testing.T) {
-       f := func(x uint8) bool {
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(uint64)
+               casted, ok := decoded.(int64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == x
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt32PositiveSymmetric(t *testing.T) {
-       f := func(x int32) bool {
-               if x == -1<<31 {
-                       return true
-               }
-               if x < 0 {
-                       x = -x
-               }
+func TestInt32Symmetric(t *testing.T) {
+       f := func(x uint32) bool {
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(uint64)
+               casted, ok := decoded.(uint64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == uint64(x)
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt32NegativeSymmetric(t *testing.T) {
+func TestInt32NSymmetric(t *testing.T) {
        f := func(x int32) bool {
-               if x > 0 {
-                       x = -x
-               }
-               if x == 0 {
+               if x >= 0 {
                        return true
                }
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(int64)
+               casted, ok := decoded.(int64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if int64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == int64(x)
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt16PositiveSymmetric(t *testing.T) {
-       f := func(x int16) bool {
-               if x == -1<<15 {
-                       return true
-               }
-               if x < 0 {
-                       x = -x
-               }
+func TestInt16Symmetric(t *testing.T) {
+       f := func(x uint16) bool {
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(uint64)
+               casted, ok := decoded.(uint64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == uint64(x)
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt16NegativeSymmetric(t *testing.T) {
+func TestInt16NSymmetric(t *testing.T) {
        f := func(x int16) bool {
-               if x > 0 {
-                       x = -x
-               }
-               if x == 0 {
+               if x >= 0 {
                        return true
                }
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(int64)
+               casted, ok := decoded.(int64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if int64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == int64(x)
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt8PositiveSymmetric(t *testing.T) {
-       f := func(x int8) bool {
-               if x == -1<<7 {
-                       return true
-               }
-               if x < 0 {
-                       x = -x
-               }
+func TestInt8Symmetric(t *testing.T) {
+       f := func(x uint8) bool {
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(uint64)
+               casted, ok := decoded.(uint64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == uint64(x)
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt8NegativeSymmetric(t *testing.T) {
+func TestInt8NSymmetric(t *testing.T) {
        f := func(x int8) bool {
-               if x > 0 {
-                       x = -x
-               }
-               if x == 0 {
+               if x >= 0 {
                        return true
                }
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(int64)
+               casted, ok := decoded.(int64)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if int64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == int64(x)
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt64PositiveSymmetric(t *testing.T) {
-       f := func(x int64) bool {
-               if x < 0 {
-                       x = -x
-               }
+func TestIntBigSymmetric(t *testing.T) {
+       f := func(l uint8) bool {
+               data := make([]byte, 8+1+int(l))
+               if _, err := io.ReadFull(PRNG, data); err != nil {
+                       panic(err)
+               }
+               data[0] |= 0x80 // be sure that is really a big number
+               x := big.NewInt(0).SetBytes(data)
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(uint64)
+               casted, ok := decoded.(*big.Int)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if uint64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted.Cmp(x) == 0
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
        }
 }
 
-func TestInt64NegativeSymmetric(t *testing.T) {
-       f := func(x int64) bool {
-               if x > 0 {
-                       x = -x
-               }
-               if x == 0 {
-                       return true
-               }
+func TestIntNBigSymmetric(t *testing.T) {
+       f := func(l uint8) bool {
+               data := make([]byte, 8+1+int(l))
+               if _, err := io.ReadFull(PRNG, data); err != nil {
+                       panic(err)
+               }
+               data[0] |= 0x80 // be sure that is really a big number
+               x := big.NewInt(0).SetBytes(data)
+               x = x.Neg(x)
                encoded, err := EncodeBuf(x, nil)
                if err != nil {
-                       t.Logf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
-
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(int64)
+               casted, ok := decoded.(*big.Int)
                if !ok {
-                       t.Logf("failed to cast")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if int64(x) != udecoded {
-                       t.Logf("Expected <%s, %d>, got <%s, %d>", reflect.TypeOf(x), x, reflect.TypeOf(decoded), decoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted.Cmp(x) == 0
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
similarity index 59%
rename from go/support_test.go
rename to go/junk_test.go
index a57611dc6c2deced88f34177c0174051b9a3ab95c3b6838e7eea189b39406fb9..7072a999241f3f115308440b9744ac4b7d716d949d1e1c602b5059baa1e38eee 100644 (file)
@@ -1,5 +1,5 @@
 // GoKEKS -- Go KEKS codec implementation
-// Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
 package keks
 
 import (
-       "crypto/rand"
-       "encoding/hex"
+       crand "crypto/rand"
        "io"
-       mrand "math/rand"
+       "math/rand/v2"
 )
 
-func mustHexDec(s string) []byte {
-       b, err := hex.DecodeString(s)
+var (
+       PRNG *rand.ChaCha8
+       Junk []byte
+)
+
+func init() {
+       var seed [32]byte
+       _, err := io.ReadFull(crand.Reader, seed[:])
        if err != nil {
                panic(err)
        }
-       return b
-}
-
-func invalidUTF8_2Byte(x, y int) []byte {
-       return []byte{1<<7 | 1<<6 | byte(x), 1<<7 | 1<<6 | byte(y)}
-}
-
-func invalidUTF8_3Byte(x, y, z int) []byte {
-       return []byte{(1<<7 | 1<<6 | 1<<5 | byte(x)), (1<<7 | byte(y)), (1<<7 | 1<<6 | byte(z))}
-}
-
-var Junk []byte
-
-func init() {
-       Junk = make([]byte, 1+mrand.Intn(8))
-       _, err := io.ReadFull(rand.Reader, Junk)
+       PRNG = rand.NewChaCha8(seed)
+       Junk = make([]byte, 1+rand.UintN(7))
+       _, err = io.ReadFull(PRNG, Junk)
        if err != nil {
                panic(err)
        }
index e28a806c00fcdcdb136d28cdc0933aba2d46fe921845b58a2ad329c9f38d0e56..1dd24647a9f15ef8a6b8d0a44a1488dfa5e3f5b45328b4f99613e8608ad5eeb3 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,71 +18,103 @@ package keks
 
 import (
        "bytes"
-       "encoding/hex"
        "io"
-       "reflect"
+       "math/rand/v2"
        "slices"
        "testing"
        "testing/quick"
 )
 
 func TestListEmpty(t *testing.T) {
-       object := []any{}
-       binstring := []byte("\x08\x00")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+       obj := []any{}
+       bin := []byte{0x08, 0x00}
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       udecoded, ok := decoded.([]any)
+       casted, ok := decoded.([]any)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
+       }
+       if !slices.Equal(obj, casted) {
+               t.Fatal("casted differs")
        }
-       if !slices.Equal(object, udecoded) {
-               t.Logf("expected type %s, value true", reflect.TypeOf(object))
-               t.Logf("got type %s, value %t", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestListThrowsWhenNoEOC(t *testing.T) {
-       binstring := []byte("\x08")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestListNoEOC(t *testing.T) {
+       encoded, err := EncodeBuf([]int{123}, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       _, err = NewDecoderFromBytes(encoded[:len(encoded)-1], nil).Decode()
        if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
+               t.Fatal(err)
+       }
+}
+
+func TestListMaxContLen(t *testing.T) {
+       l := 2 + rand.UintN(8)
+       bin, err := EncodeBuf(make([]int, l), nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       decoder := NewDecoderFromBytes(bin, nil)
+       _, err = decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       decoder = NewDecoderFromBytes(bin, &DecodeOpts{MaxContLen: int64(l) + 1})
+       _, err = decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       decoder = NewDecoderFromBytes(bin, &DecodeOpts{MaxContLen: int64(l)})
+       _, err = decoder.Decode()
+       if err != ErrLenTooBig {
+               t.Fatal(err)
        }
 }
 
 func TestListIntSymmetric(t *testing.T) {
-       f := func(x []uint64) bool {
-               encoded, err := EncodeBuf(x, nil)
+       f := func(vs []int64) bool {
+               encoded, err := EncodeBuf(vs, nil)
                if err != nil {
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.([]any)
+               casted, ok := decoded.([]any)
                if !ok {
-                       t.Fatalf("failed to cast")
+                       t.Fatal("failed to cast")
                }
-               if len(x) != len(udecoded) {
+               if len(vs) != len(casted) {
                        return false
                }
-               for i := range x {
-                       if x[i] != udecoded[i] {
-                               t.Logf("Expected <%s, %d>", reflect.TypeOf(x), x)
-                               t.Logf("Instead  <%s, %d>", reflect.TypeOf(decoded), decoded)
+               for i := range len(vs) {
+                       switch v := casted[i].(type) {
+                       case uint64:
+                               if v != uint64(vs[i]) {
+                                       return false
+                               }
+                       case int64:
+                               if v != int64(vs[i]) {
+                                       return false
+                               }
+                       default:
                                return false
                        }
                }
diff --git a/go/magic_test.go b/go/magic_test.go
new file mode 100644 (file)
index 0000000..4efe285
--- /dev/null
@@ -0,0 +1,74 @@
+// GoKEKS -- Go KEKS codec implementation
+// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package keks
+
+import (
+       "bytes"
+       "io"
+       "strings"
+       "testing"
+       "testing/quick"
+)
+
+func TestMagicSymmetric(t *testing.T) {
+       f := func(m [12]byte) bool {
+               encoded, err := EncodeBuf(Magic(m[:]), nil)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               casted, ok := decoded.(Magic)
+               if !ok {
+                       t.Fatal("failed to cast")
+               }
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
+               }
+               return string(casted) == strings.TrimRight(string(m[:]), "\x00")
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestMagicNotEnoughData(t *testing.T) {
+       encoded, err := EncodeBuf(Magic("something"), nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       decoder := NewDecoderFromBytes(encoded[:len(encoded)-1], nil)
+       _, err = decoder.Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
+       }
+}
+
+func TestMagicBadMagic(t *testing.T) {
+       encoded, err := EncodeBuf(Magic("something"), nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded[2] = 'e'
+       decoder := NewDecoderFromBytes(encoded, nil)
+       _, err = decoder.Decode()
+       if err != ErrBadMagic {
+               t.Fatal(err)
+       }
+}
index d3de8069192746404d29e43f499024af87d01788bd9448c0cbf7494494c52f52..9ae869396d72eea80d7142b5d90fa08152b3dc386f330b27b92c12f80c5bb7e1 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,109 +18,166 @@ package keks
 
 import (
        "bytes"
-       "encoding/hex"
-       "maps"
-       "reflect"
+       "math/rand/v2"
+       "strconv"
        "testing"
        "testing/quick"
 )
 
 func TestMapEmpty(t *testing.T) {
-       binstring := []byte("\x09\x00")
-       object := make(map[string]any)
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+       bin := []byte("\x09\x00")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       encoded, err := EncodeBuf(object, nil)
+       encoded, err := EncodeBuf(struct{}{}, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
        }
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
-       }
-       udecoded, ok := decoded.(map[string]any)
+       casted, ok := decoded.(map[string]any)
        if !ok {
-               t.Fatalf("failed to cast")
+               t.Fatal("failed to cast")
+       }
+       if len(casted) != 0 {
+               t.Fatal("casted differs")
        }
-       if !maps.Equal(object, udecoded) {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.Fail()
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
        }
 }
 
-func TestMapThrowsWhenNonStringKey(t *testing.T) {
-       object := make(map[any]any)
-       object[1] = "a"
-       binstring := []byte("\x09\x0C\x80\xC6value2\x00")
-       _, err := EncodeBuf(object, nil)
+func TestMapEncodeNonStrKey(t *testing.T) {
+       _, err := EncodeBuf(map[any]any{123: 123}, nil)
        if err != ErrMapBadKey {
-               t.Fatalf("expected ErrMapBadKey during encode, got %v", err)
+               t.Fatal(err)
        }
-       _, err = NewDecoderFromBytes(binstring, nil).Decode()
+}
+
+func TestMapDecodeNonStrKey(t *testing.T) {
+       _, err := NewDecoderFromBytes([]byte("\x09\x0C\x80\xC6value2\x00"), nil).Decode()
        if err != ErrMapBadKey {
-               t.Fatalf("expected ErrMapBadKey during decode, got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestMapThrowsWhenUnexpectedEOC(t *testing.T) {
-       binstring := []byte("\x09\xC4key1\x00\x00")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestMapUnexpectedEOC(t *testing.T) {
+       _, err := NewDecoderFromBytes([]byte("\x09\xC4key1\x00\x00"), nil).Decode()
        if err != ErrUnexpectedEOC {
-               t.Fatalf("expected ErrUnexpectedEOC during decode, got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestMapThrowsWhenEmptyStringKeyEncode(t *testing.T) {
-       object := make(map[any]any)
-       object[""] = "a"
-       _, err := EncodeBuf(object, nil)
+func TestMapEncodeEmptyStrKey(t *testing.T) {
+       _, err := EncodeBuf(map[string]any{"": 123}, nil)
        if err != ErrMapBadKey {
-               t.Fatalf("expected ErrMapBadKey during encode, got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestMapThrowsWhenEmptyStringKeyDecode(t *testing.T) {
-       binstring := []byte("\x09\xC0\x0C\x81\x01\xC4key1\xC6value1\x00")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestMapDecodeEmptyStrKey(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec("09C0C000"), nil).Decode()
        if err != ErrMapBadKey {
-               t.Fatalf("expected ErrMapBadKey during decode, got %v", err)
+               t.Fatal(err)
+       }
+}
+
+func TestMapUnsortedValueKeys(t *testing.T) {
+       bin := bytes.Join([][]byte{
+               mustHexDec("09"),
+               mustHexDec("C4"), []byte("key2"),
+               mustHexDec("0C8101"),
+               mustHexDec("C4"), []byte("key1"),
+               mustHexDec("C6"), []byte("value1"),
+               mustHexDec("00"),
+       }, nil)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
+       if err != ErrMapUnordered {
+               t.Fatal(err)
        }
 }
 
-func TestMapThrowsWhenUnsortedKeys(t *testing.T) {
-       binstring := []byte("\x09\xC4key2\xC6value2\xC4key1\xC6value1\x00")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestMapUnsortedLenKeys(t *testing.T) {
+       bin := bytes.Join([][]byte{
+               mustHexDec("09"),
+               mustHexDec("C4"), []byte("key2"),
+               mustHexDec("0C8101"),
+               mustHexDec("C3"), []byte("key"),
+               mustHexDec("C6"), []byte("value1"),
+               mustHexDec("00"),
+       }, nil)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != ErrMapUnordered {
-               t.Fatalf("expected ErrMapUnordered during decode, got %v", err)
+               t.Fatal(err)
+       }
+}
+
+func TestMapMaxContLen(t *testing.T) {
+       l := 2 + rand.UintN(8)
+       m := make(map[string]int, l)
+       for i := range l {
+               m[strconv.Itoa(int(i))] = 123
+       }
+       bin, err := EncodeBuf(m, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       decoder := NewDecoderFromBytes(bin, nil)
+       _, err = decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       decoder = NewDecoderFromBytes(bin, &DecodeOpts{MaxContLen: int64(l) + 1})
+       _, err = decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       decoder = NewDecoderFromBytes(bin, &DecodeOpts{MaxContLen: int64(l)})
+       _, err = decoder.Decode()
+       if err != ErrLenTooBig {
+               t.Fatal(err)
        }
 }
 
 func TestMapIntSymmetric(t *testing.T) {
-       f := func(x map[string]uint64) bool {
-               delete(x, "")
-               encoded, err := EncodeBuf(x, nil)
+       f := func(m map[int]uint64) bool {
+               obj := make(map[string]uint64, len(m))
+               for k, v := range m {
+                       obj[strconv.Itoa(k)] = v
+               }
+               m = nil
+               encoded, err := EncodeBuf(obj, nil)
                if err != nil {
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(map[string]any)
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
+               }
+               casted, ok := decoded.(map[string]any)
                if !ok {
-                       t.Logf("failed to cast")
+                       t.Fatal("failed to cast")
+               }
+               if len(casted) != len(obj) {
                        return false
                }
-               for k := range x {
-                       if x[k] != udecoded[k] {
-                               t.Logf("expected <%s, %v>", reflect.TypeOf(x), x)
-                               t.Logf("instead  <%s, %v>", reflect.TypeOf(udecoded), udecoded)
+               for k := range obj {
+                       v, ok := casted[k]
+                       if !ok {
+                               return false
+                       }
+                       i, ok := v.(uint64)
+                       if !ok {
+                               t.Fatal("failed to cast")
+                       }
+                       if i != obj[k] {
                                return false
                        }
                }
index 5a7daeeb5b657d357946aab4522803e70bb338b7253aa95b26ff4de4d7096bf4..3426f8c6db2aa6f5c189e056657c46ac999d8dba64daf83c3cf69d2bdcde4a9f 100644 (file)
@@ -121,6 +121,12 @@ func (ctx *Decoder) parse() (t types.Type, err error) {
                                return
                        }
                        count++
+                       if ctx.opts != nil &&
+                               ctx.opts.MaxContLen > 0 &&
+                               int64(count) >= ctx.opts.MaxContLen {
+                               err = ErrLenTooBig
+                               return
+                       }
                }
                ctx.lens[idx] = count
                ctx.depth--
diff --git a/go/str_test.go b/go/str_test.go
new file mode 100644 (file)
index 0000000..273a104
--- /dev/null
@@ -0,0 +1,397 @@
+// GoKEKS -- Go KEKS codec implementation
+// Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package keks
+
+import (
+       "bytes"
+       "io"
+       "math/rand"
+       "reflect"
+       "strings"
+       "testing"
+       "testing/quick"
+
+       "go.cypherpunks.su/keks/be"
+)
+
+func TestStrEmpty(t *testing.T) {
+       obj := ""
+       bin := mustHexDec("C0")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded, err := EncodeBuf(obj, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(string)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != obj {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+}
+
+func TestStrHelloWorld(t *testing.T) {
+       obj := "hello world"
+       bin := append([]byte{0xCB}, []byte("hello world")...)
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded, err := EncodeBuf(obj, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(string)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != obj {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+}
+
+func TestStrПриветМир(t *testing.T) {
+       obj := "привет Ð¼Ð¸Ñ€"
+       bin := append([]byte{0xD3}, []byte("привет Ð¼Ð¸Ñ€")...)
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded, err := EncodeBuf(obj, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(string)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != obj {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+}
+
+func TestStrLen62(t *testing.T) {
+       obj := strings.Repeat("a", 62)
+       bin := mustHexDec("FD01" + strings.Repeat("61", 62))
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded, err := EncodeBuf(obj, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(string)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != obj {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+}
+
+func TestStrLen318(t *testing.T) {
+       obj := strings.Repeat("a", 62+255+1)
+       bin := mustHexDec("FE0001" + strings.Repeat("61", 62+255+1))
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded, err := EncodeBuf(obj, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(string)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != obj {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+}
+
+func TestStrLen65853(t *testing.T) {
+       obj := strings.Repeat("a", 62+255+65535+1)
+       bin := mustHexDec("FF0000000000000000" + strings.Repeat("61", 62+255+65535+1))
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
+       if err != nil {
+               t.Fatal(err)
+       }
+       encoded, err := EncodeBuf(obj, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+       casted, ok := decoded.(string)
+       if !ok {
+               t.Fatal("failed to cast")
+       }
+       if casted != obj {
+               t.Fatal("casted differs")
+       }
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+}
+
+func TestStrNullByteInUTF8(t *testing.T) {
+       bin := []byte("\xC5he\x00\x01\x02")
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
+       if err != ErrBadUTF8 {
+               t.Fatal(err)
+       }
+}
+
+func TestStrNullByteInUTF8AndCheckDisabled(t *testing.T) {
+       bin := []byte("\xC5he\x00\x01\x02")
+       _, err := NewDecoderFromBytes(bin, &DecodeOpts{DisableUTF8Check: true}).Decode()
+       if err == ErrBadUTF8 {
+               t.Fatal(err)
+       }
+}
+
+// Here go invalid UTF-8 byte(s) strategies. We can fail in following
+// ways during decoding:
+// * invalid start byte -- hypothesis generates integers that will
+//   satisfy that fail
+// * invalid continuation byte -- we strip last byte of multibyte
+//   sequences
+// * unexpected end of data -- we strip the last byte of our data
+
+func TestStrStripped2ByteInUTF8(t *testing.T) {
+       // 110xxxxx 10xxxxxx(STRIPPED)
+       f := func(x int) bool {
+               bin := []byte{0xC1, (1 << 7) | (1 << 6) | byte(x)}
+               _, err := NewDecoderFromBytes(bin, nil).Decode()
+               if err != ErrBadUTF8 {
+                       return false
+               }
+               _, err = NewDecoderFromBytes(bin,
+                       &DecodeOpts{DisableUTF8Check: true}).Decode()
+               return err == nil
+       }
+       if err := quick.Check(f, &quick.Config{
+               Values: func(v []reflect.Value, r *rand.Rand) {
+                       v[0] = reflect.ValueOf(r.Intn((1 << 5) - 1))
+               },
+       }); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestStrStripped3ByteInUTF8(t *testing.T) {
+       // 1110xxxx 10xxxxxx 10xxxxxx(STRIPPED)
+       f := func(x int) bool {
+               bin := []byte{
+                       0xC2,
+                       (1 << 7) | (1 << 6) | (1 << 5) | byte(x>>6),
+                       byte((1 << 7) | (((1 << 6) - 1) & x)),
+               }
+               _, err := NewDecoderFromBytes(bin, nil).Decode()
+               if err != ErrBadUTF8 {
+                       return false
+               }
+               _, err = NewDecoderFromBytes(bin,
+                       &DecodeOpts{DisableUTF8Check: true}).Decode()
+               return err == nil
+       }
+       if err := quick.Check(f, &quick.Config{
+               Values: func(v []reflect.Value, r *rand.Rand) {
+                       v[0] = reflect.ValueOf(r.Intn((1 << 10) - 1))
+               },
+       }); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestStrNotEnoughData(t *testing.T) {
+       bin, err := EncodeBuf("hello", nil)
+       if err != nil {
+               panic(err)
+       }
+       _, err = NewDecoderFromBytes(bin[:len(bin)-1], nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
+       }
+}
+
+func TestStrNotEnoughDataForLength8(t *testing.T) {
+       bin, err := EncodeBuf(strings.Repeat("f", 100), nil)
+       if err != nil {
+               panic(err)
+       }
+       _, err = NewDecoderFromBytes(bin[:len(bin)-1], nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
+       }
+}
+
+func TestStrNotEnoughDataForLength16(t *testing.T) {
+       bin, err := EncodeBuf(strings.Repeat("f", 318), nil)
+       if err != nil {
+               panic(err)
+       }
+       _, err = NewDecoderFromBytes(bin[:len(bin)-1], nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
+       }
+}
+
+func TestStrNotEnoughDataForLength64(t *testing.T) {
+       bin, err := EncodeBuf(strings.Repeat("f", 65853), nil)
+       if err != nil {
+               panic(err)
+       }
+       _, err = NewDecoderFromBytes(bin[:len(bin)-1], nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
+       }
+}
+
+func TestStrTooLong(t *testing.T) {
+       bin := make([]byte, 9)
+       bin[0] = 0xFF
+       for _, l := range []uint64{
+               1 << 63,
+               (1 << 63) - 63,
+               (1 << 63) - 63 - ((1 << 8) - 1),
+               (1 << 63) - 63 - ((1 << 8) - 1) - ((1 << 16) - 1) + 1,
+       } {
+               be.Put(bin[1:], l)
+               decoder := NewDecoderFromBytes(bin, nil)
+               _, err := decoder.Decode()
+               if err != ErrLenTooBig {
+                       t.Fatal(err)
+               }
+       }
+}
+
+func TestStrNotTooLong(t *testing.T) {
+       bin := make([]byte, 9)
+       bin[0] = 0xFF
+       be.Put(bin[1:], (1<<63)-63-((1<<8)-1)-((1<<16)-1))
+       decoder := NewDecoderFromReader(bytes.NewReader(bin), nil)
+       defer func() { recover() }()
+       _, err := decoder.Decode()
+       // it won't reach that code because it can not allocate so much
+       // memory in .getBytes(), but that means, that no error was returned
+       t.Fatal(err)
+}
+
+func TestStrMaxStrLen(t *testing.T) {
+       f := func(str string) bool {
+               if str == "" {
+                       return true
+               }
+               encoded, err := EncodeBuf(str, nil)
+               if err != nil {
+                       panic(err)
+               }
+               decoder := NewDecoderFromBytes(encoded,
+                       &DecodeOpts{DisableUTF8Check: true, MaxStrLen: int64(len(str) + 1)})
+               _, err = decoder.Decode()
+               if err != nil {
+                       return false
+               }
+               decoder = NewDecoderFromBytes(encoded,
+                       &DecodeOpts{DisableUTF8Check: true, MaxStrLen: int64(len(str))})
+               _, err = decoder.Decode()
+               if err != nil {
+                       return false
+               }
+               if len(str) == 1 {
+                       return true
+               }
+               decoder = NewDecoderFromBytes(encoded,
+                       &DecodeOpts{DisableUTF8Check: true, MaxStrLen: int64(len(str) - 1)})
+               _, err = decoder.Decode()
+               return err != nil
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestStrSymmetric(t *testing.T) {
+       f := func(str string) bool {
+               encoded, err := EncodeBuf(str, nil)
+               if err != nil {
+                       panic(err)
+               }
+               decoder := NewDecoderFromBytes(
+                       append(encoded, Junk...),
+                       &DecodeOpts{DisableUTF8Check: true},
+               )
+               decoded, err := decoder.Decode()
+               if err != nil {
+                       return false
+               }
+               casted, ok := decoded.(string)
+               if !ok {
+                       return false
+               }
+               if str != casted {
+                       return false
+               }
+               return bytes.Equal(decoder.B, Junk)
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Fatal(err)
+       }
+}
diff --git a/go/string_test.go b/go/string_test.go
deleted file mode 100644 (file)
index 8826260..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-// GoKEKS -- Go KEKS codec implementation
-// Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation, version 3 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-package keks
-
-import (
-       "bytes"
-       "encoding/hex"
-       "io"
-       mrand "math/rand"
-       "reflect"
-       "strings"
-       "testing"
-       "testing/quick"
-)
-
-func TestStrEmpty(t *testing.T) {
-       object := ""
-       binstring := mustHexDec("C0")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
-       }
-       encoded, err := EncodeBuf(object, nil)
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
-       }
-       if decoded != object {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
-       }
-}
-
-func TestStrHelloWorld(t *testing.T) {
-       object := "hello world"
-       binstring := bytes.Join([][]byte{mustHexDec("CB"), []byte("hello world")}, nil)
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
-       }
-       encoded, err := EncodeBuf(object, nil)
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
-       }
-       if decoded != object {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
-       }
-}
-
-func TestStrRuHelloWorld(t *testing.T) {
-       object := "привет Ð¼Ð¸Ñ€"
-       binstring := bytes.Join([][]byte{mustHexDec("D3"), []byte("привет Ð¼Ð¸Ñ€")}, nil)
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
-       }
-       encoded, err := EncodeBuf(object, nil)
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
-       }
-       if decoded != object {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
-       }
-}
-
-func TestStrLen62(t *testing.T) {
-       object := strings.Repeat("a", 62)
-       binstring := mustHexDec(
-               strings.Join([]string{"FD01", strings.Repeat("61", 62)}, ""),
-       )
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
-       }
-       encoded, err := EncodeBuf(object, nil)
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
-       }
-       if decoded != object {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
-       }
-}
-
-func TestStrLen318(t *testing.T) {
-       object := strings.Repeat("a", 318)
-       binstring := mustHexDec(
-               strings.Join([]string{"FE0001", strings.Repeat("61", 318)}, ""),
-       )
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
-       }
-       encoded, err := EncodeBuf(object, nil)
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected binstring %s, got %s", hex.EncodeToString(binstring), hex.EncodeToString(encoded))
-       }
-       if decoded != object {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
-       }
-}
-
-func TestStrLen65853(t *testing.T) {
-       object := strings.Repeat("a", 65853)
-       binstring := mustHexDec(
-               strings.Join([]string{"FF0000000000000000", strings.Repeat("61", 65853)}, ""),
-       )
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
-       }
-       encoded, err := EncodeBuf(object, nil)
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
-       }
-       if !bytes.Equal(encoded, binstring) {
-               t.Logf("expected binstring to begin with %s, got %s", hex.EncodeToString(binstring)[:40], hex.EncodeToString(encoded)[:40])
-               t.Fail()
-       }
-       if decoded != object {
-               t.Logf("expected type %s, value %s", reflect.TypeOf(object), object)
-               t.Logf("got type %s, value %s", reflect.TypeOf(decoded), decoded)
-               t.FailNow()
-       }
-}
-
-func TestStrThrowsWhenNullByteInUTF8(t *testing.T) {
-       binstring := []byte("\xC5he\x00\x01\x02")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != ErrBadUTF8 {
-               t.Fatalf("expected ErrBadUTF8, got %v", err)
-       }
-}
-
-func TestStrOkWhenNullByteInUTF8AndUTF8CheckDisabled(t *testing.T) {
-       binstring := []byte("\xC5he\xe2\x82\x28")
-       _, err := NewDecoderFromBytes(binstring, &DecodeOpts{DisableUTF8Check: true}).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %v", err)
-       }
-}
-
-func TestStrThrowsWhenInvalid2BytesInUTF8(t *testing.T) {
-       f := func(x, y int) bool {
-               binstring := bytes.Join([][]byte{[]byte("\xC2"), invalidUTF8_2Byte(x, y)}, nil)
-               _, err := NewDecoderFromBytes(binstring, nil).Decode()
-               if err != ErrBadUTF8 {
-                       t.Logf("expected ErrBadUTF8, got %v", err)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, &quick.Config{
-               Values: func(v []reflect.Value, r *mrand.Rand) {
-                       v[0] = reflect.ValueOf(r.Intn(1 << 5))
-                       v[1] = reflect.ValueOf(r.Intn(1 << 5))
-               },
-       }); err != nil {
-               t.Fatal(err)
-       }
-}
-
-func TestStrOkWhenInvalid2BytesInUTF8AndUTF8CheckDisabled(t *testing.T) {
-       f := func(x, y int) bool {
-               _, err := NewDecoderFromBytes(bytes.Join([][]byte{[]byte("\xC2"), invalidUTF8_2Byte(x, y)}, nil), &DecodeOpts{DisableUTF8Check: true}).Decode()
-               if err != nil {
-                       t.Logf("error during decode: %v", err)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, &quick.Config{
-               Values: func(v []reflect.Value, r *mrand.Rand) {
-                       v[0] = reflect.ValueOf(r.Intn(1 << 5))
-                       v[1] = reflect.ValueOf(r.Intn(1 << 5))
-               },
-       }); err != nil {
-               t.Fatal(err)
-       }
-}
-
-func TestStrThrowsWhenInvalid3BytesInUTF8(t *testing.T) {
-       f := func(x, y, z int) bool {
-               binstring := bytes.Join([][]byte{[]byte("\xC3"), invalidUTF8_3Byte(x, y, z)}, nil)
-               _, err := NewDecoderFromBytes(binstring, nil).Decode()
-               if err != ErrBadUTF8 {
-                       t.Logf("expected ErrBadUTF8, got %v", err)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, &quick.Config{
-               Values: func(v []reflect.Value, r *mrand.Rand) {
-                       v[0] = reflect.ValueOf(r.Intn(1 << 4))
-                       v[1] = reflect.ValueOf(r.Intn(1 << 6))
-                       v[2] = reflect.ValueOf(r.Intn(1 << 6))
-               },
-       }); err != nil {
-               t.Fatal(err)
-       }
-}
-
-func TestStrOkWhenInvalid3BytesInUTF8AndUTF8CheckDisabled(t *testing.T) {
-       f := func(x, y, z int) bool {
-               binstring := bytes.Join([][]byte{[]byte("\xC3"), invalidUTF8_3Byte(x, y, z)}, nil)
-               _, err := NewDecoderFromBytes(binstring, &DecodeOpts{DisableUTF8Check: true}).Decode()
-               if err != nil {
-                       t.Logf("error during decode: %v", err)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, &quick.Config{
-               Values: func(v []reflect.Value, r *mrand.Rand) {
-                       v[0] = reflect.ValueOf(r.Intn(1 << 4))
-                       v[1] = reflect.ValueOf(r.Intn(1 << 6))
-                       v[2] = reflect.ValueOf(r.Intn(1 << 6))
-               },
-       }); err != nil {
-               t.Fatal(err)
-       }
-}
-
-func TestStrThrowsWhenNotEnoughData(t *testing.T) {
-       binstring := []byte("\xC5he")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
-       }
-}
-
-func TestStrThrowsWhenNotEnoughDataForLength8(t *testing.T) {
-       binstring := []byte("\xC5he")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
-       }
-}
-
-func TestStrThrowsWhenNotEnoughDataForLength16(t *testing.T) {
-       binstring := []byte("\xC5he")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
-       }
-}
-
-func TestThrowsWhenNotEnoughDataForLength64(t *testing.T) {
-       binstring := []byte("\xC5he")
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != io.ErrUnexpectedEOF {
-               t.Fatalf("expected io.ErrUnexpectedEOF, got %v", err)
-       }
-}
-
-func TestStrThrowsWhenStringTooLong(t *testing.T) {
-       f := func(str string, y uint8) bool {
-               if str == "" {
-                       return true
-               }
-               y = y % uint8(len(str))
-               if y == 0 {
-                       return true
-               }
-               encoded, _ := EncodeBuf(str, nil)
-               decoder := NewDecoderFromBytes(encoded, &DecodeOpts{MaxStrLen: int64(len(str) - int(y))})
-               _, err := decoder.Decode()
-               if err != ErrLenTooBig {
-                       t.Logf("expected ErrLenTooBig, got %v", err)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, nil); err != nil {
-               t.Fatal(err)
-       }
-}
-
-func TestStrOkWhenMaxStrLenSet(t *testing.T) {
-       f := func(str string, y uint8) bool {
-               encoded, err := EncodeBuf(str, nil)
-               if err != nil {
-                       return false
-               }
-               decoder := NewDecoderFromBytes(encoded, &DecodeOpts{MaxStrLen: int64(len(str) + int(y))})
-               decoded, err := decoder.Decode()
-               if err != nil {
-                       return false
-               }
-               if str != decoded.(string) {
-                       t.Logf("Expected <%s, %s>", reflect.TypeOf(str), str)
-                       t.Logf("Instead  <%s, %s>", reflect.TypeOf(decoded), decoded)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, nil); err != nil {
-               t.Fatal(err)
-       }
-}
-
-func TestStrSymmetric(t *testing.T) {
-       f := func(str string) bool {
-               encoded, err := EncodeBuf(str, nil)
-               if err != nil {
-                       return false
-               }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
-               if err != nil {
-                       return false
-               }
-               if str != decoded.(string) {
-                       t.Logf("Expected <%s, %s>", reflect.TypeOf(str), str)
-                       t.Logf("Instead  <%s, %s>", reflect.TypeOf(decoded), decoded)
-                       return false
-               }
-               return true
-       }
-       if err := quick.Check(f, nil); err != nil {
-               t.Fatal(err)
-       }
-}
index ca533b12504fa9215be2de002afdd3656f06b14b20a3a845cbb4e2efa919292c..92b9182113ab43d8977c83c79cf6f36537ccf5f8d27915fe7caa4342c6549fc1 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,265 +18,466 @@ package keks
 
 import (
        "bytes"
+       "io"
+       "strings"
        "testing"
        "testing/quick"
        "time"
+
+       "go.cypherpunks.su/keks/be"
+       "go.cypherpunks.su/tai64n/v4"
 )
 
-func TestTai64LargeNumberOfSeconds(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("18"),
-               mustHexDec("7000000065195F65"),
-       }, nil)
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+const hex1G = "3B9ACA00" // one billion
+
+func TestTAINotEnoughData(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "18"+strings.Repeat("0", 2*8-2)), nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
+}
+
+func TestTAINNotEnoughData(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "19"+strings.Repeat("0", 2*12-2)), nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
        }
-       t.Log(decoded)
 }
 
-func TestTai64NLargeNumberOfSeconds(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("19"),
-               mustHexDec("7000000065195F65"),
-               mustHexDec("3B9AC9FF"),
-       }, nil)
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != nil {
-               t.Fatalf("error during decode: %s", err)
+func TestTAINANotEnoughData(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "1A"+strings.Repeat("0", 2*16-2)), nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
        }
+}
+
+func TestTAILargeNumberOfSeconds(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "18"+"7000000065195F65"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       t.Log(decoded)
 }
 
-func TestTai64NALargeNumberOfSeconds(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("1A"),
-               mustHexDec("7000000065195F66"),
-               mustHexDec("3B9AC9FF"),
-               mustHexDec("3B9AC9FF"),
-       }, nil)
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAINLargeNumberOfSeconds(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "19"+"7000000065195F65"+"00010000"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
+}
+
+func TestTAINALargeNumberOfSeconds(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "1A"+"7000000065195F65"+"00010000"+"00010000"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       t.Log(decoded)
 }
 
-func TestTaiThrowsWhenMsbIsSet(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("18"),
-               mustHexDec("8000000065195F65"),
-       }, nil)
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != ErrTaiReserved {
-               t.Fatalf("expected 'reserved TAI64 value is in use', got %v", err)
+func TestTAIMsbIsSet(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "18"+"8000000065195F65"), nil).Decode()
+       if err != ErrTAIReserved {
+               t.Fatal(err)
+       }
+}
+
+func TestTAINMsbIsSet(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "19"+"8000000065195F65"+"00010000"), nil).Decode()
+       if err != ErrTAIReserved {
+               t.Fatal(err)
+       }
+}
+
+func TestTAINAMsbIsSet(t *testing.T) {
+       _, err := NewDecoderFromBytes(mustHexDec(
+               "19"+"8000000065195F65"+"00010000"+"00010000"), nil).Decode()
+       if err != ErrTAIReserved {
+               t.Fatal(err)
        }
 }
 
-func TestTai64NThrowsWhenTooManyNanosecs(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("19"),
-               mustHexDec("4000000000000000"),
-               mustHexDec("3B9ACA00"),
-       }, nil)
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAINTooManyNanosecs(t *testing.T) {
+       bin := mustHexDec("19" + "4000000000000000" + hex1G)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != ErrTooManyNsecs {
-               t.Fatalf("expected 'too many nanoseconds', got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestTai64NThrowsWhenZeroNanoseconds(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("19"),
-               mustHexDec("4000000000000000"),
-               mustHexDec("00000000"),
-       }, nil)
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != ErrTaiNonMinimal {
-               t.Fatalf("expected 'non-minimal TAI64N', got %v", err)
+func TestTAINATooManyNanosecs(t *testing.T) {
+       bin := mustHexDec("1A" + "4000000000000000" + hex1G + "00010000")
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
+       if err != ErrTooManyNsecs {
+               t.Fatal(err)
        }
 }
 
-func TestTai64NAThrowsWhenTooManyAttoseconds(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("1A"),
-               mustHexDec("4000000000000000"),
-               mustHexDec("00000000"),
-               mustHexDec("3B9ACA00"),
-       }, nil)
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAINATooManyAttosecs(t *testing.T) {
+       bin := mustHexDec("1A" + "4000000000000000" + "00010000" + hex1G)
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != ErrTooManyAsecs {
-               t.Fatalf("expected 'too many attoseconds', got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestTai64NAThrowsWhenZeroAttoseconds(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("1A"),
-               mustHexDec("4000000000000000"),
-               mustHexDec("00000000"),
-               mustHexDec("00000000"),
-       }, nil)
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != ErrTaiNonMinimal {
-               t.Fatalf("expected 'non-minimal TAI64NA', got %v", err)
+func TestTAINZeroNanosecs(t *testing.T) {
+       bin := mustHexDec("19" + "4000000000000000" + "00000000")
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
+       if err != ErrTAINonMinimal {
+               t.Fatal(err)
        }
 }
 
-func TestTai64NAZeroNanosecondsOk(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("1A"),
-               mustHexDec("4000000000000000"),
-               mustHexDec("00000000"),
-               mustHexDec("00000001"),
-       }, nil)
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAINAZeroNanosecsOk(t *testing.T) {
+       bin := mustHexDec("1A" + "4000000000000000" + "00000000" + "00000001")
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
        if err != nil {
-               t.Fatalf("expected no error, got %v", err)
+               t.Fatal(err)
        }
 }
 
-func TestTai64NAThrowsWhenMsbIsSet(t *testing.T) {
-       binstring := bytes.Join([][]byte{
-               mustHexDec("1A"),
-               mustHexDec("8000000065195F65"),
-               mustHexDec("00000001"),
-               mustHexDec("00000001")}, nil)
-       _, err := NewDecoderFromBytes(binstring, nil).Decode()
-       if err != ErrTaiReserved {
-               t.Fatalf("expected 'reserved TAI64 value is in use', got %v", err)
+func TestTAINAZeroAttosecs(t *testing.T) {
+       bin := mustHexDec("1A" + "4000000000000000" + "00000000" + "00000000")
+       _, err := NewDecoderFromBytes(bin, nil).Decode()
+       if err != ErrTAINonMinimal {
+               t.Fatal(err)
        }
 }
 
-func TestTai19700101(t *testing.T) {
-       binstring := mustHexDec("18400000000000000A")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAI19700101(t *testing.T) {
+       decoded, err := NewDecoderFromBytes(mustHexDec(
+               "18400000000000000A"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during decode: %v", err)
+       casted, ok := decoded.(time.Time)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       expected := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
-       decodedTime := decoded.(time.Time)
-       if !decodedTime.Equal(expected) {
-               t.Fatalf("expected %v, got %v", expected, decoded)
+       if !casted.Equal(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)) {
+               t.Fatal("casted differs")
        }
 }
 
-func TestTai19920602(t *testing.T) {
-       binstring := mustHexDec("18400000002A2B2C2D")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAI19920602(t *testing.T) {
+       decoded, err := NewDecoderFromBytes(mustHexDec(
+               "18400000002A2B2C2D"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       decodedTime := decoded.(time.Time)
-       if err != nil {
-               t.Fatalf("error during decode: %v", err)
+       casted, ok := decoded.(time.Time)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       expected := time.Date(1992, 6, 2, 8, 6, 43, 0, time.UTC)
-       if !decodedTime.Equal(expected) {
-               t.Fatalf("expected %v, got %v", expected, decoded)
+       if !casted.Equal(time.Date(1992, 6, 2, 8, 6, 43, 0, time.UTC)) {
+               t.Fatal("casted differs")
        }
 }
 
-func TestTai19971003(t *testing.T) {
-       binstring := mustHexDec("184000000034353637")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAI19971003(t *testing.T) {
+       decoded, err := NewDecoderFromBytes(mustHexDec(
+               "184000000034353637"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       decodedTime := decoded.(time.Time)
-       if err != nil {
-               t.Fatalf("error during decode: %v", err)
+       casted, ok := decoded.(time.Time)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       expected := time.Date(1997, 10, 3, 18, 14, 48, 0, time.UTC)
-       if !decodedTime.Equal(expected) {
-               t.Fatalf("expected %v, got %v", expected, decoded)
+       if !casted.Equal(time.Date(1997, 10, 3, 18, 14, 48, 0, time.UTC)) {
+               t.Fatal("casted differs")
        }
 }
 
-func TestTai20161231(t *testing.T) {
-       binstring := mustHexDec("1840000000586846A3")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAI20161231(t *testing.T) {
+       decoded, err := NewDecoderFromBytes(mustHexDec(
+               "1840000000586846A3"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       decodedTime := decoded.(time.Time)
-       if err != nil {
-               t.Fatalf("error during decode: %v", err)
+       casted, ok := decoded.(time.Time)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       expected := time.Date(2016, 12, 31, 23, 59, 59, 0, time.UTC)
-       if !decodedTime.Equal(expected) {
-               t.Fatalf("expected %v, got %v", expected, decoded)
+       if !casted.Equal(time.Date(2016, 12, 31, 23, 59, 59, 0, time.UTC)) {
+               t.Fatal("casted differs")
        }
 }
 
-func TestTai20170101(t *testing.T) {
-       binstring := mustHexDec("1840000000586846A5")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAI20170101(t *testing.T) {
+       decoded, err := NewDecoderFromBytes(mustHexDec(
+               "1840000000586846A5"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       decodedTime := decoded.(time.Time)
-       if err != nil {
-               t.Fatalf("error during decode: %v", err)
+       casted, ok := decoded.(time.Time)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       expected := time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
-       if !decodedTime.Equal(expected) {
-               t.Fatalf("expected %v, got %v", expected, decoded)
+       if !casted.Equal(time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)) {
+               t.Fatal("casted differs")
        }
 }
 
-func TestTai20241120(t *testing.T) {
-       binstring := mustHexDec("1940000000673DD3E136F11FE0")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+func TestTAI20241120(t *testing.T) {
+       decoded, err := NewDecoderFromBytes(mustHexDec(
+               "1940000000673DD3E136F11FE0"), nil).Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       decodedTime := decoded.(time.Time)
-       if err != nil {
-               t.Fatalf("error during decode: %v", err)
+       casted, ok := decoded.(time.Time)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       expected := time.Date(2024, 11, 20, 12, 19, 8, 921772000, time.UTC)
-       if !decodedTime.Equal(expected) {
-               t.Fatalf("expected %v, got %v", expected, decoded)
+       if !casted.Equal(time.Date(2024, 11, 20, 12, 19, 8, 921772000, time.UTC)) {
+               t.Fatal("casted differs")
        }
 }
 
-func TestTaiSymmetric(t *testing.T) {
-       f := func(a, b int64) bool {
-               x := time.Unix(a, b)
-               if a > 1<<62-1 {
+func TestTAISymmetric(t *testing.T) {
+       f := func(secs int64) bool {
+               if (secs > (1<<62)-1) || (secs < -(1 << 62)) {
                        return true
                }
-               if a < -1<<62 {
-                       return true
+               tm := time.Unix(secs, 0)
+               encoded, err := EncodeBuf(tm, nil)
+               if err != nil {
+                       t.Fatal(err)
                }
-               encoded, err := EncodeBuf(x, nil)
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Logf("error during encode: %s", err)
+                       t.Fatal(err)
+               }
+               casted, ok := decoded.(time.Time)
+               if !ok {
+                       t.Fatal("failed to cast")
+               }
+               if !casted.Equal(tm) {
                        return false
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               if !bytes.Equal(decoder.B, Junk) {
+                       return false
+               }
+               decoder = NewDecoderFromBytes(append(encoded, Junk...), &DecodeOpts{
+                       LeaveTAI: true,
+               })
+               decoded, err = decoder.Decode()
                if err != nil {
-                       t.Logf("error during decode: %s", err)
+                       t.Fatal(err)
+               }
+               casted, ok = decoded.(time.Time)
+               if !ok {
+                       t.Fatal("failed to cast")
+               }
+               {
+                       tai, isLeap := tai64n.Leapsecs.Sub(casted)
+                       if isLeap {
+                               return false
+                       }
+                       if !tai.Equal(tm) {
+                               return false
+                       }
+                       if !bytes.Equal(decoder.B, Junk) {
+                               return false
+                       }
+                       {
+                               var encoded2 []byte
+                               encoded2, err = EncodeBuf(casted, nil)
+                               if err != nil {
+                                       t.Fatal(err)
+                               }
+                               if bytes.Equal(encoded, encoded2) {
+                                       return false
+                               }
+                               encoded2, err = EncodeBuf(casted, &EncodeOpts{LeaveTAI: true})
+                               if err != nil {
+                                       t.Fatal(err)
+                               }
+                               if !bytes.Equal(encoded, encoded2) {
+                                       return false
+                               }
+                       }
+               }
+               decoder = NewDecoderFromBytes(append(encoded, Junk...), &DecodeOpts{
+                       LeaveTAI64: true,
+               })
+               decoded, err = decoder.Decode()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               {
+                       tai, ok := decoded.(*tai64n.TAI64)
+                       if !ok {
+                               t.Fatal("failed to cast")
+                       }
+                       if !bytes.Equal(encoded[1:], tai[:]) {
+                               return false
+                       }
+                       var raw []byte
+                       raw, err = EncodeBuf(tai, nil)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if !bytes.Equal(raw, encoded) {
+                               return false
+                       }
+               }
+               return bytes.Equal(decoder.B, Junk)
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestTAINSymmetric(t *testing.T) {
+       f := func(secs int64, nsecs uint32) bool {
+               if (secs > (1<<62)-1) || (secs < -(1 << 62)) {
+                       return true
+               }
+               nsecs %= 1000
+               if nsecs == 0 {
+                       return true
+               }
+               tm := time.Unix(secs, int64(nsecs))
+               encoded, err := EncodeBuf(tm, nil)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               casted, ok := decoded.(time.Time)
+               if !ok {
+                       t.Fatal("failed to cast")
+               }
+               if !casted.Equal(tm) {
+                       return false
+               }
+               if !bytes.Equal(decoder.B, Junk) {
                        return false
                }
-               udecoded, ok := decoded.(time.Time)
+               decoder = NewDecoderFromBytes(append(encoded, Junk...), &DecodeOpts{
+                       LeaveTAI: true,
+               })
+               decoded, err = decoder.Decode()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               casted, ok = decoded.(time.Time)
                if !ok {
+                       t.Fatal("failed to cast")
+               }
+               {
+                       tai, isLeap := tai64n.Leapsecs.Sub(casted)
+                       if isLeap {
+                               return false
+                       }
+                       if !tai.Equal(tm) {
+                               return false
+                       }
+                       if !bytes.Equal(decoder.B, Junk) {
+                               return false
+                       }
+                       {
+                               var encoded2 []byte
+                               encoded2, err = EncodeBuf(casted, nil)
+                               if err != nil {
+                                       t.Fatal(err)
+                               }
+                               if bytes.Equal(encoded, encoded2) {
+                                       return false
+                               }
+                               encoded2, err = EncodeBuf(casted, &EncodeOpts{LeaveTAI: true})
+                               if err != nil {
+                                       t.Fatal(err)
+                               }
+                               if !bytes.Equal(encoded, encoded2) {
+                                       return false
+                               }
+                       }
+               }
+               decoder = NewDecoderFromBytes(append(encoded, Junk...), &DecodeOpts{
+                       LeaveTAI64: true,
+               })
+               decoded, err = decoder.Decode()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               {
+                       tai, ok := decoded.(*tai64n.TAI64N)
+                       if !ok {
+                               t.Fatal("failed to cast")
+                       }
+                       if !bytes.Equal(encoded[1:], tai[:]) {
+                               return false
+                       }
+                       var raw []byte
+                       raw, err = EncodeBuf(tai, nil)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if !bytes.Equal(raw, encoded) {
+                               return false
+                       }
+               }
+               return bytes.Equal(decoder.B, Junk)
+       }
+       if err := quick.Check(f, nil); err != nil {
+               t.Fatal(err)
+       }
+}
+
+func TestTAINASymmetric(t *testing.T) {
+       f := func(secs int64, nsecs, asecs uint32) bool {
+               if (secs > (1<<62)-1) || (secs < -(1 << 62)) {
+                       return true
+               }
+               nsecs %= 1000
+               asecs %= 1000
+               if asecs == 0 {
+                       return true
+               }
+               secs += tai64n.Base
+               encoded := make([]byte, 1+tai64n.TAI64NASize)
+               encoded[0] = byte(AtomTAI64NA)
+               be.Put(encoded[1:1+8], uint64(secs))
+               be.Put(encoded[1+8:1+8+4], uint64(nsecs))
+               be.Put(encoded[1+8+4:1+8+4+4], uint64(asecs))
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
+               if err != nil {
+                       t.Fatal(err)
+               }
+               casted, ok := decoded.(*tai64n.TAI64NA)
+               if !ok {
+                       t.Fatal("failed to cast")
+               }
+               if !bytes.Equal(casted[:], encoded[1:]) {
                        return false
                }
-               return x.Equal(udecoded)
+               if !bytes.Equal(decoder.B, Junk) {
+                       return false
+               }
+               {
+                       var raw []byte
+                       raw, err = EncodeBuf(casted, nil)
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       if !bytes.Equal(raw, encoded) {
+                               return false
+                       }
+               }
+               return true
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
index 9dd3fc302f0d465eb928bdab358f15e97b2dded4f02b931c10957f94db15d8a5..fc14c915c8b860d663dcbb43a767e626ef65a348b112dfca68d335725ae9d18d 100644 (file)
@@ -1,5 +1,6 @@
 // GoKEKS -- Go KEKS codec implementation
 // Copyright (C) 2024-2025 Anton Rudenko <rudenko.ad@phystech.edu>
+//                         Sergey Matveev <stargrave@stargrave.org>
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU Lesser General Public License as
@@ -17,7 +18,7 @@ package keks
 
 import (
        "bytes"
-       "reflect"
+       "io"
        "testing"
        "testing/quick"
 
@@ -25,50 +26,59 @@ import (
 )
 
 func TestUUIDEncodeDecode(t *testing.T) {
-       object, _ := uuid.Parse("12345678-1234-5678-1234-567812345678")
-       binstring := mustHexDec("0412345678123456781234567812345678")
-       decoded, err := NewDecoderFromBytes(binstring, nil).Decode()
+       obj := uuid.MustParse("12345678-1234-5678-1234-567812345678")
+       bin := mustHexDec("0412345678123456781234567812345678")
+       decoder := NewDecoderFromBytes(append(bin, Junk...), nil)
+       decoded, err := decoder.Decode()
        if err != nil {
-               t.Fatalf("error during decode: %s", err)
+               t.Fatal(err)
        }
-       if err != nil {
-               t.Fatalf("error during encode: %s", err)
+       casted, ok := decoded.(uuid.UUID)
+       if !ok {
+               t.Fatal("failed to cast")
        }
-       if decoded != object {
-               t.Fatalf("expected %v, got %v", object, decoded)
+       if casted != obj {
+               t.Fatal("casted differs")
        }
-       encoded, err := EncodeBuf(object, nil)
+       if !bytes.Equal(decoder.B, Junk) {
+               t.Fatal("tail differs")
+       }
+       encoded, err := EncodeBuf(obj, nil)
        if err != nil {
-               t.Fatalf("error during encode: %s", err)
+               t.Fatal(err)
        }
-       if !bytes.Equal(encoded, binstring) {
-               t.Fatalf("expected %v, got %v", binstring, encoded)
+       if !bytes.Equal(encoded, bin) {
+               t.Fatal("encoded differs")
+       }
+}
+
+func TestUUIDNotEnoughData(t *testing.T) {
+       bin := mustHexDec("0412345678123456781234567812345678")
+       _, err := NewDecoderFromBytes(bin[:len(bin)-4], nil).Decode()
+       if err != io.ErrUnexpectedEOF {
+               t.Fatal(err)
        }
 }
 
 func TestUUIDSymmetric(t *testing.T) {
-       f := func(object uuid.UUID) bool {
-               encoded, err := EncodeBuf(object, nil)
+       f := func(obj uuid.UUID) bool {
+               encoded, err := EncodeBuf(obj, nil)
                if err != nil {
-                       t.Fatalf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               decoded, err := NewDecoderFromBytes(encoded, nil).Decode()
+               decoder := NewDecoderFromBytes(append(encoded, Junk...), nil)
+               decoded, err := decoder.Decode()
                if err != nil {
-                       t.Fatalf("error during encode: %s", err)
-                       return false
+                       t.Fatal(err)
                }
-               udecoded, ok := decoded.(uuid.UUID)
+               casted, ok := decoded.(uuid.UUID)
                if !ok {
-                       t.Fatalf("failed to cast to uuid.UUID")
-                       return false
+                       t.Fatal("failed to cast")
                }
-               if object != udecoded {
-                       t.Logf("expected <%s, %d>", reflect.TypeOf(object), object)
-                       t.Logf("instead  <%s, %d>", reflect.TypeOf(udecoded), udecoded)
-                       return false
+               if !bytes.Equal(decoder.B, Junk) {
+                       t.Fatal("tail differs")
                }
-               return true
+               return casted == obj
        }
        if err := quick.Check(f, nil); err != nil {
                t.Fatal(err)
index 3c0bc569b75bd7b4e1a4f49bc1fea39d2d5f662a9558f66bab88a4ca8dd90f1c..2237e68c6f67b5164472c77e8756f5d652f4dd1e4e38ecd4090122700e83ae68 100644 (file)
@@ -116,7 +116,7 @@ class TestInt(TestCase):
             loads(bytes.fromhex("0C8301"))
         self.assertEqual(err.exception.n, 2)
 
-    def test_throws_when_unminimal_int(self) -> None:
+    def test_throws_when_nonminimal_int(self) -> None:
         with self.assertRaises(DecodeError) as err:
             encoded: bytes = bytes.fromhex("0C81007B")
             loads(encoded)
index 379a9416cdb0d769f7fb46f558a145e1fb8244558d4707db477fa351372f6ff0..7b98eb3230ccd87774f94a1fe6754d25b9c7a80edf05720db2083cb7dec7709b 100644 (file)
@@ -121,14 +121,7 @@ class TestMap(TestCase):
         self.assertEqual(str(err.exception), "map keys can not be empty")
 
     def test_throws_when_decoding_empty_str_as_key(self):
-        encoded = b"".join((
-            bytes.fromhex("09"),
-            bytes.fromhex("C0"),
-            bytes.fromhex("0C8101"),
-            bytes.fromhex("C4"), b"key1",
-            bytes.fromhex("C6"), b"value1",
-            bytes.fromhex("00"),
-        ))
+        encoded = bytes.fromhex("09" + "C0" + "C0" + "00")
         with self.assertRaises(DecodeError) as err:
             decoded, tail = loads(encoded)
         self.assertEqual(str(err.exception), "empty key")
index 7edcfe578100231f7dac5005e4c546e667f918dff66d95a4948f119102934fa4..a37edcfe3a7e9e0abbb1a0b2dd3fdf52508e7d7e4e2eb0d279825a07aaa4ed64 100644 (file)
@@ -109,7 +109,7 @@ class TestStr(TestCase):
         self.assertSequenceEqual(decoded, s)
         self.assertSequenceEqual(tail, junk)
 
-    def test_throws_when_null_byte_in_utf(self) -> None:
+    def test_throws_when_null_byte_in_utf8(self) -> None:
         with self.assertRaises(DecodeError) as err:
             result, tail = loads(b"\xc5he\x00\x01\x02")
         self.assertEqual(str(err.exception), "null byte in UTF-8")