Repository: fxamacker/cbor
Pull request #267 (2021-04-22 02:24:35)

Add low-level API to StreamEncoder


Patch:
diff --git a/encode.go b/encode.go
index ebd903a..158f951 100644
--- a/encode.go
+++ b/encode.go
@@ -1264,24 +1264,24 @@ func encodeHead(e *encoderBuffer, t byte, n uint64) {
 		return
 	}
 	if n <= math.MaxUint8 {
-		e.scratch[0] = t | byte(24)
+		e.scratch[0] = t | 24
 		e.scratch[1] = byte(n)
 		e.Write(e.scratch[:2])
 		return
 	}
 	if n <= math.MaxUint16 {
-		e.scratch[0] = t | byte(25)
+		e.scratch[0] = t | 25
 		binary.BigEndian.PutUint16(e.scratch[1:], uint16(n))
 		e.Write(e.scratch[:3])
 		return
 	}
 	if n <= math.MaxUint32 {
-		e.scratch[0] = t | byte(26)
+		e.scratch[0] = t | 26
 		binary.BigEndian.PutUint32(e.scratch[1:], uint32(n))
 		e.Write(e.scratch[:5])
 		return
 	}
-	e.scratch[0] = t | byte(27)
+	e.scratch[0] = t | 27
 	binary.BigEndian.PutUint64(e.scratch[1:], n)
 	e.Write(e.scratch[:9])
 }
diff --git a/stream_encode.go b/stream_encode.go
index f8d9c73..51e969b 100644
--- a/stream_encode.go
+++ b/stream_encode.go
@@ -3,7 +3,10 @@
 
 package cbor
 
-import "io"
+import (
+	"io"
+	"math/big"
+)
 
 // StreamEncoder provides low-level API for sequential encoding.
 //
@@ -18,9 +21,14 @@ type StreamEncoder struct {
 
 // NewStreamEncoder returns a new StreamEncoder for sequential encoding.
 func NewStreamEncoder(w io.Writer) *StreamEncoder {
-	enc := defaultEncMode.NewEncoder(w)
-	enc.stream = true
-	return &StreamEncoder{enc}
+	return &StreamEncoder{
+		Encoder: &Encoder{
+			w:      w,
+			em:     defaultEncMode,
+			e:      getEncoderBuffer(),
+			stream: true,
+		},
+	}
 }
 
 // Flush writes streamed data to underlying io.Writer.
@@ -30,11 +38,153 @@ func (se *StreamEncoder) Flush() error {
 }
 
 // EncodeArrayHead encodes CBOR array head of specified size.
-func (se *StreamEncoder) EncodeArrayHead(size uint64) {
+func (se *StreamEncoder) EncodeArrayHead(size uint64) error {
 	encodeHead(se.Encoder.e, byte(cborTypeArray), size)
+	return nil
 }
 
 // EncodeTagHead encodes CBOR tag head with num as tag number.
-func (se *StreamEncoder) EncodeTagHead(num uint64) {
+func (se *StreamEncoder) EncodeTagHead(num uint64) error {
 	encodeHead(se.Encoder.e, byte(cborTypeTag), num)
+	return nil
+}
+
+// EncodeRawBytes writes b to the underlying writer.
+// If b is an empty or nil byte slice, it is a no-op.
+func (se *StreamEncoder) EncodeRawBytes(b []byte) error {
+	if len(b) == 0 {
+		return nil
+	}
+	se.e.Write(b)
+	return nil
+}
+
+// EncodeNil encodes CBOR nil.
+func (se *StreamEncoder) EncodeNil() error {
+	se.e.Write(cborNil)
+	return nil
+}
+
+// EncodeBool encodes bool as CBOR bool.
+func (se *StreamEncoder) EncodeBool(b bool) error {
+	bytes := cborTrue
+	if !b {
+		bytes = cborFalse
+	}
+	se.e.Write(bytes)
+	return nil
+}
+
+// EncodeUint encodes uint as CBOR positive integer.
+func (se *StreamEncoder) EncodeUint(i uint) error {
+	encodeHead(se.e, byte(cborTypePositiveInt), uint64(i))
+	return nil
+}
+
+// EncodeUint8 encodes uint8 as CBOR positive integer.
+func (se *StreamEncoder) EncodeUint8(i uint8) error {
+	encodeHead(se.e, byte(cborTypePositiveInt), uint64(i))
+	return nil
+}
+
+// EncodeUint16 encodes uint16 as CBOR positive integer.
+func (se *StreamEncoder) EncodeUint16(i uint16) error {
+	encodeHead(se.e, byte(cborTypePositiveInt), uint64(i))
+	return nil
+}
+
+// EncodeUint32 encodes uint32 as CBOR positive integer.
+func (se *StreamEncoder) EncodeUint32(i uint32) error {
+	encodeHead(se.e, byte(cborTypePositiveInt), uint64(i))
+	return nil
+}
+
+// EncodeUint64 encodes uint64 as CBOR positive integer.
+func (se *StreamEncoder) EncodeUint64(i uint64) error {
+	encodeHead(se.e, byte(cborTypePositiveInt), i)
+	return nil
+}
+
+// EncodeInt encodes int as CBOR positive or negtive integer.
+func (se *StreamEncoder) EncodeInt(i int) error {
+	return se.EncodeInt64(int64(i))
+}
+
+// EncodeInt8 encodes int8 as CBOR positive or negtive integer.
+func (se *StreamEncoder) EncodeInt8(i int8) error {
+	return se.EncodeInt64(int64(i))
+}
+
+// EncodeInt16 encodes int16 as CBOR positive or negtive integer.
+func (se *StreamEncoder) EncodeInt16(i int16) error {
+	return se.EncodeInt64(int64(i))
+}
+
+// EncodeInt32 encodes int32 as CBOR positive or negtive integer.
+func (se *StreamEncoder) EncodeInt32(i int32) error {
+	return se.EncodeInt64(int64(i))
+}
+
+// EncodeInt64 encodes int64 as CBOR positive or negtive integer.
+func (se *StreamEncoder) EncodeInt64(i int64) error {
+	t := cborTypePositiveInt
+	if i < 0 {
+		t = cborTypeNegativeInt
+		i = i*(-1) - 1
+	}
+	encodeHead(se.e, byte(t), uint64(i))
+	return nil
+}
+
+// EncodeBytes encodes byte slice as CBOR byte string.
+func (se *StreamEncoder) EncodeBytes(b []byte) error {
+	if b == nil {
+		se.e.Write(cborNil)
+		return nil
+	}
+
+	encodeHead(se.e, byte(cborTypeByteString), uint64(len(b)))
+	se.e.Write(b)
+	return nil
+}
+
+// EncodeString encodes string as CBOR string.
+func (se *StreamEncoder) EncodeString(s string) error {
+	encodeHead(se.e, byte(cborTypeTextString), uint64(len(s)))
+	se.e.WriteString(s)
+	return nil
+}
+
+var bigOne = big.NewInt(1)
+
+// EncodeBigInt encodes big.Int as CBOR bignum (tag number 2 and 3).
+func (se *StreamEncoder) EncodeBigInt(v *big.Int) error {
+	if se.em.bigIntConvert == BigIntConvertShortest {
+		if v.IsUint64() {
+			encodeHead(se.e, byte(cborTypePositiveInt), v.Uint64())
+			return nil
+		}
+		if v.IsInt64() {
+			return se.EncodeInt64(v.Int64())
+		}
+	}
+
+	tagNum := 2
+	sign := v.Sign()
+	if sign < 0 {
+		tagNum = 3
+
+		// Create a new big.Int with value of -1 - v
+		v = new(big.Int).Abs(v)
+		v.Sub(v, bigOne)
+	}
+
+	b := v.Bytes()
+
+	// Write tag number
+	encodeHead(se.e, byte(cborTypeTag), uint64(tagNum))
+	// Write bignum byte string
+	encodeHead(se.e, byte(cborTypeByteString), uint64(len(b)))
+	se.e.Write(b)
+	return nil
 }


Test patch:
diff --git a/decode_test.go b/decode_test.go
index 21e7c80..4506169 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -1367,6 +1367,11 @@ var invalidCBORUnmarshalTests = []struct {
 	{"Major type 0 with additional information 31", hexDecode("1f"), "cbor: invalid additional information 31 for type positive integer", true},
 	{"Major type 1 with additional information 31", hexDecode("3f"), "cbor: invalid additional information 31 for type negative integer", true},
 	{"Major type 6 with additional information 31", hexDecode("df"), "cbor: invalid additional information 31 for type tag", true},
+	// more
+	{"End of input in a head", hexDecode("59"), "unexpected EOF", false},
+	{"End of input in a head", hexDecode("5b"), "unexpected EOF", false},
+	{"End of input in a head", hexDecode("d8"), "unexpected EOF", false},
+	{"End of input in a head", hexDecode("d9"), "unexpected EOF", false},
 }
 
 func TestInvalidCBORUnmarshal(t *testing.T) {
@@ -1680,6 +1685,7 @@ func TestLengthOverflowsInt(t *testing.T) {
 	// Data is generating by go-fuzz.
 	// string/slice/map length in uint64 cast to int causes integer overflow.
 	cborData := [][]byte{
+		hexDecode("9bcf30303030303030cfd697829782"),
 		hexDecode("bbcf30303030303030cfd697829782"),
 		hexDecode("5bcf30303030303030cfd697829782"),
 	}
diff --git a/stream_encode_test.go b/stream_encode_test.go
index 60c1ab2..e94a5a1 100644
--- a/stream_encode_test.go
+++ b/stream_encode_test.go
@@ -10,6 +10,8 @@ import (
 )
 
 func TestStreamEncodeArray(t *testing.T) {
+	t.Parallel()
+
 	t.Run("default mode", func(t *testing.T) {
 		expected := []byte{
 			// array, 2 items follow
@@ -27,23 +29,29 @@ func TestStreamEncodeArray(t *testing.T) {
 		var buf bytes.Buffer
 		se := NewStreamEncoder(&buf)
 
-		se.EncodeArrayHead(2)
+		err := se.EncodeArrayHead(2)
+		if err != nil {
+			t.Errorf("EncodeArrayHead() returned error %v", err)
+		}
 
-		err := se.Encode("hello")
+		err = se.Encode("hello")
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
-		se.EncodeArrayHead(1)
+		err = se.EncodeArrayHead(1)
+		if err != nil {
+			t.Errorf("EncodeArrayHead() returned error %v", err)
+		}
 
 		err = se.Encode(big.NewInt(1))
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
 		err = se.Flush()
 		if err != nil {
-			t.Errorf("StreamEncoder.Flush() returned error %v", err)
+			t.Errorf("Flush() returned error %v", err)
 		}
 
 		if !bytes.Equal(buf.Bytes(), expected) {
@@ -77,23 +85,29 @@ func TestStreamEncodeArray(t *testing.T) {
 		var buf bytes.Buffer
 		se := em.NewStreamEncoder(&buf)
 
-		se.EncodeArrayHead(2)
+		err = se.EncodeArrayHead(2)
+		if err != nil {
+			t.Errorf("EncodeArrayHead() returned error %v", err)
+		}
 
 		err = se.Encode("hello")
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
-		se.EncodeArrayHead(1)
+		err = se.EncodeArrayHead(1)
+		if err != nil {
+			t.Errorf("EncodeArrayHead() returned error %v", err)
+		}
 
 		err = se.Encode(big.NewInt(1))
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
 		err = se.Flush()
 		if err != nil {
-			t.Errorf("StreamEncoder.Flush() returned error %v", err)
+			t.Errorf("Flush() returned error %v", err)
 		}
 
 		if !bytes.Equal(buf.Bytes(), expected) {
@@ -103,6 +117,8 @@ func TestStreamEncodeArray(t *testing.T) {
 }
 
 func TestStreamEncodeTag(t *testing.T) {
+	t.Parallel()
+
 	t.Run("default mode", func(t *testing.T) {
 		expected := []byte{
 			// tag 128
@@ -120,23 +136,29 @@ func TestStreamEncodeTag(t *testing.T) {
 		var buf bytes.Buffer
 		se := NewStreamEncoder(&buf)
 
-		se.EncodeTagHead(128)
+		err := se.EncodeTagHead(128)
+		if err != nil {
+			t.Errorf("EncodeTagHead() returned error %v", err)
+		}
 
-		se.EncodeArrayHead(2)
+		err = se.EncodeArrayHead(2)
+		if err != nil {
+			t.Errorf("EncodeArrayHead() returned error %v", err)
+		}
 
-		err := se.Encode("hello")
+		err = se.Encode("hello")
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
 		err = se.Encode(big.NewInt(1))
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
 		err = se.Flush()
 		if err != nil {
-			t.Errorf("StreamEncoder.Flush() returned error %v", err)
+			t.Errorf("Flush() returned error %v", err)
 		}
 
 		if !bytes.Equal(buf.Bytes(), expected) {
@@ -170,23 +192,29 @@ func TestStreamEncodeTag(t *testing.T) {
 		var buf bytes.Buffer
 		se := em.NewStreamEncoder(&buf)
 
-		se.EncodeTagHead(128)
+		err = se.EncodeTagHead(128)
+		if err != nil {
+			t.Errorf("EncodeTagHead() returned error %v", err)
+		}
 
-		se.EncodeArrayHead(2)
+		err = se.EncodeArrayHead(2)
+		if err != nil {
+			t.Errorf("EncodeArrayHead() returned error %v", err)
+		}
 
 		err = se.Encode("hello")
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
 		err = se.Encode(big.NewInt(1))
 		if err != nil {
-			t.Errorf("StreamEncoder.Encode() returned error %v", err)
+			t.Errorf("Encode() returned error %v", err)
 		}
 
 		err = se.Flush()
 		if err != nil {
-			t.Errorf("StreamEncoder.Flush() returned error %v", err)
+			t.Errorf("Flush() returned error %v", err)
 		}
 
 		if !bytes.Equal(buf.Bytes(), expected) {
@@ -194,3 +222,653 @@ func TestStreamEncodeTag(t *testing.T) {
 		}
 	})
 }
+
+func TestStreamEncodeNil(t *testing.T) {
+	expected := []byte{0xf6}
+
+	var buf bytes.Buffer
+	se := NewStreamEncoder(&buf)
+
+	err := se.EncodeNil()
+	if err != nil {
+		t.Errorf("EncodeNil() returned error %v", err)
+	}
+
+	err = se.Flush()
+	if err != nil {
+		t.Errorf("Flush() returned error %v", err)
+	}
+
+	if !bytes.Equal(buf.Bytes(), expected) {
+		t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), expected)
+	}
+}
+
+func TestStreamEncodeRawBytes(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    []byte
+		expected []byte
+	}{
+		{"nil", nil, []byte{}},
+		{"empty", []byte{}, []byte{}},
+		{"not empty", []byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeRawBytes(tc.value)
+			if err != nil {
+				t.Errorf("EncodeRawBytes() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeBool(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    bool
+		expected []byte
+	}{
+		{"false", false, []byte{0xf4}},
+		{"true", true, []byte{0xf5}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeBool(tc.value)
+			if err != nil {
+				t.Errorf("EncodeBool() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeUint(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    uint
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+		{"1", 1, []byte{0x01}},
+		{"255", 255, []byte{0x18, 0xff}},
+		{"65535", 65535, []byte{0x19, 0xff, 0xff}},
+		{"4294967295", 4294967295, []byte{0x1a, 0xff, 0xff, 0xff, 0xff}},
+		{"18446744073709551615", 18446744073709551615, []byte{0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeUint(tc.value)
+			if err != nil {
+				t.Errorf("EncodeUint() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeUint8(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    uint8
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+		{"1", 1, []byte{0x01}},
+		{"100", 100, []byte{0x18, 0x64}},
+		{"255", 255, []byte{0x18, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeUint8(tc.value)
+			if err != nil {
+				t.Errorf("EncodeUint8() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeUint16(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    uint16
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+		{"1", 1, []byte{0x01}},
+		{"255", 255, []byte{0x18, 0xff}},
+		{"65535", 65535, []byte{0x19, 0xff, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeUint16(tc.value)
+			if err != nil {
+				t.Errorf("EncodeUint16() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeUint32(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    uint32
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+		{"1", 1, []byte{0x01}},
+		{"255", 255, []byte{0x18, 0xff}},
+		{"65535", 65535, []byte{0x19, 0xff, 0xff}},
+		{"4294967295", 4294967295, []byte{0x1a, 0xff, 0xff, 0xff, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeUint32(tc.value)
+			if err != nil {
+				t.Errorf("EncodeUint32() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeUint64(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    uint64
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+		{"1", 1, []byte{0x01}},
+		{"255", 255, []byte{0x18, 0xff}},
+		{"65535", 65535, []byte{0x19, 0xff, 0xff}},
+		{"4294967295", 4294967295, []byte{0x1a, 0xff, 0xff, 0xff, 0xff}},
+		{"18446744073709551615", 18446744073709551615, []byte{0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeUint64(tc.value)
+			if err != nil {
+				t.Errorf("EncodeUint64() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeInt(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    int
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+
+		{"-1", -1, []byte{0x20}},
+		{"1", 1, []byte{0x01}},
+
+		{"-128", -128, []byte{0x38, 0x7f}},
+		{"127", 127, []byte{0x18, 0x7f}},
+
+		{"-32768", -32768, []byte{0x39, 0x7f, 0xff}},
+		{"32767", 32767, []byte{0x19, 0x7f, 0xff}},
+
+		{"-2147483648", -2147483648, []byte{0x3a, 0x7f, 0xff, 0xff, 0xff}},
+		{"2147483647", 2147483647, []byte{0x1a, 0x7f, 0xff, 0xff, 0xff}},
+
+		{"-9223372036854775808", -9223372036854775808, []byte{0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+		{"9223372036854775807", 9223372036854775807, []byte{0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeInt(tc.value)
+			if err != nil {
+				t.Errorf("EncodeInt() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeInt8(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    int8
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+
+		{"-1", -1, []byte{0x20}},
+		{"1", 1, []byte{0x01}},
+
+		{"-128", -128, []byte{0x38, 0x7f}},
+		{"127", 127, []byte{0x18, 0x7f}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeInt8(tc.value)
+			if err != nil {
+				t.Errorf("EncodeInt8() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeInt16(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    int16
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+
+		{"-1", -1, []byte{0x20}},
+		{"1", 1, []byte{0x01}},
+
+		{"-128", -128, []byte{0x38, 0x7f}},
+		{"127", 127, []byte{0x18, 0x7f}},
+
+		{"-32768", -32768, []byte{0x39, 0x7f, 0xff}},
+		{"32767", 32767, []byte{0x19, 0x7f, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeInt16(tc.value)
+			if err != nil {
+				t.Errorf("EncodeInt16() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeInt32(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    int32
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+
+		{"-1", -1, []byte{0x20}},
+		{"1", 1, []byte{0x01}},
+
+		{"-128", -128, []byte{0x38, 0x7f}},
+		{"127", 127, []byte{0x18, 0x7f}},
+
+		{"-32768", -32768, []byte{0x39, 0x7f, 0xff}},
+		{"32767", 32767, []byte{0x19, 0x7f, 0xff}},
+
+		{"-2147483648", -2147483648, []byte{0x3a, 0x7f, 0xff, 0xff, 0xff}},
+		{"2147483647", 2147483647, []byte{0x1a, 0x7f, 0xff, 0xff, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeInt32(tc.value)
+			if err != nil {
+				t.Errorf("EncodeInt32() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeInt64(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    int64
+		expected []byte
+	}{
+		{"0", 0, []byte{0x00}},
+
+		{"-1", -1, []byte{0x20}},
+		{"1", 1, []byte{0x01}},
+
+		{"-128", -128, []byte{0x38, 0x7f}},
+		{"127", 127, []byte{0x18, 0x7f}},
+
+		{"-32768", -32768, []byte{0x39, 0x7f, 0xff}},
+		{"32767", 32767, []byte{0x19, 0x7f, 0xff}},
+
+		{"-2147483648", -2147483648, []byte{0x3a, 0x7f, 0xff, 0xff, 0xff}},
+		{"2147483647", 2147483647, []byte{0x1a, 0x7f, 0xff, 0xff, 0xff}},
+
+		{"-9223372036854775808", -9223372036854775808, []byte{0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+		{"9223372036854775807", 9223372036854775807, []byte{0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeInt64(tc.value)
+			if err != nil {
+				t.Errorf("EncodeInt64() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeBigIntConvertNone(t *testing.T) {
+	big1, _ := new(big.Int).SetString("18446744073709551616", 10)  // overflows uint64
+	big2, _ := new(big.Int).SetString("-18446744073709551617", 10) // overflows int64
+
+	testCases := []struct {
+		name     string
+		value    *big.Int
+		expected []byte
+	}{
+		{"0", big.NewInt(0), []byte{0xc2, 0x40}},
+		{"1", big.NewInt(1), []byte{0xc2, 0x41, 0x01}},
+		{"-1", big.NewInt(-1), []byte{0xc3, 0x40}},
+		{"18446744073709551616", big1, []byte{0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+		{"-18446744073709551617", big2, []byte{0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dm, _ := EncOptions{BigIntConvert: BigIntConvertNone}.EncMode()
+
+			var buf bytes.Buffer
+			se := dm.NewStreamEncoder(&buf)
+
+			err := se.EncodeBigInt(tc.value)
+			if err != nil {
+				t.Errorf("EncodeBigInt() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeBigIntConvertShortest(t *testing.T) {
+	big1, _ := new(big.Int).SetString("18446744073709551616", 10)  // overflows uint64
+	big2, _ := new(big.Int).SetString("-18446744073709551617", 10) // overflows int64
+
+	testCases := []struct {
+		name     string
+		value    *big.Int
+		expected []byte
+	}{
+		{"0", big.NewInt(0), []byte{0x00}},
+		{"1", big.NewInt(1), []byte{0x01}},
+		{"-1", big.NewInt(-1), []byte{0x20}},
+		{"18446744073709551616", big1, []byte{0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+		{"-18446744073709551617", big2, []byte{0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			dm, _ := EncOptions{BigIntConvert: BigIntConvertShortest}.EncMode()
+
+			var buf bytes.Buffer
+			se := dm.NewStreamEncoder(&buf)
+
+			err := se.EncodeBigInt(tc.value)
+			if err != nil {
+				t.Errorf("EncodeBigInt() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeBytes(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    []byte
+		expected []byte
+	}{
+		{"nil", nil, []byte{0xf6}},
+		{"empty", []byte{}, []byte{0x40}},
+		{"not empty", []byte{0x01, 0x02, 0x03, 0x04, 0x05}, []byte{0x45, 0x01, 0x02, 0x03, 0x04, 0x05}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeBytes(tc.value)
+			if err != nil {
+				t.Errorf("EncodeBytes() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
+
+func TestStreamEncodeString(t *testing.T) {
+	testCases := []struct {
+		name     string
+		value    string
+		expected []byte
+	}{
+		{"empty", "", []byte{0x60}},
+		{"not empty", "hello", []byte{0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f}},
+	}
+
+	t.Parallel()
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var buf bytes.Buffer
+			se := NewStreamEncoder(&buf)
+
+			err := se.EncodeString(tc.value)
+			if err != nil {
+				t.Errorf("EncodeString() returned error %v", err)
+			}
+
+			err = se.Flush()
+			if err != nil {
+				t.Errorf("Flush() returned error %v", err)
+			}
+
+			if !bytes.Equal(buf.Bytes(), tc.expected) {
+				t.Errorf("StreamEncoder encoded 0x%x, want 0x%x", buf.Bytes(), tc.expected)
+			}
+		})
+	}
+}
diff --git a/valid_test.go b/valid_test.go
index 109ca2c..6d1f47f 100644
--- a/valid_test.go
+++ b/valid_test.go
@@ -124,7 +124,7 @@ func TestDepthError(t *testing.T) {
 		},
 		{
 			name:         "33-level map",
-			cborData:     hexDecode("a101818181818181818181818181818181818181818181818181818181818181818101"),
+			cborData:     hexDecode("a101a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a101"),
 			opts:         DecOptions{},
 			wantErrorMsg: "cbor: exceeded max nested level 32",
 		},
@@ -145,7 +145,7 @@ func TestDepthError(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			dm, _ := tc.opts.decMode()
 			d := decoder{data: tc.cborData, dm: dm}
-			if _, err := d.validInternal(0); err == nil {
+			if err := d.valid(); err == nil {
 				t.Errorf("valid(0x%x) didn't return an error", tc.cborData)
 			} else if _, ok := err.(*MaxNestedLevelError); !ok {
 				t.Errorf("valid(0x%x) returned wrong error type %T, want (*MaxNestedLevelError)", tc.cborData, err)