Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions mli.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"encoding/hex"
"errors"
"fmt"
"io"
"strconv"
)

Expand Down Expand Up @@ -357,3 +358,53 @@ func validateLength(length int, minLength, maxLength int64) error {
}
return nil
}

// EncodeWriter encodes the length into the MLI selected by key and writes it to w.
// length must exclude the MLI itself. Returns [ErrLength] when length is negative
// or outside the selected format's range.
func EncodeWriter(w io.Writer, key string, length int) error {
b, err := Encode(key, length)
if err != nil {
return err
}
_, err = w.Write(b)
return err
}

// DecodeReader reads the MLI bytes from r for the given key and returns the decoded message length.
// Returns [ErrByteSize] if the reader doesn't provide enough bytes.
// Returns [ErrLength] if the decoded value is invalid for the MLI type.
func DecodeReader(r io.Reader, key string) (int, error) {
size := mliSize(key)
if size == 0 {
return 0, fmt.Errorf("Invalid MLI type provided")
}

b := make([]byte, size)
if _, err := io.ReadFull(r, b); err != nil {
return 0, ErrByteSize
}

return Decode(key, &b)
}

func mliSize(key string) int {
switch key {
case MLI2I:
return Size2I
case MLI2E:
return Size2E
case MLI4I:
return Size4I
case MLI4E:
return Size4E
case MLI2EE:
return Size2EE
case MLI2BCD2:
return Size2BCD2
case MLIA4E:
return SizeA4E
default:
return 0
}
}
115 changes: 115 additions & 0 deletions mli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package simplemli

import (
"bytes"
"encoding/hex"
"errors"
"testing"
Expand Down Expand Up @@ -361,3 +362,117 @@ func FuzzDecode(f *testing.F) {
}
})
}

func TestEncodeWriter(t *testing.T) {
tests := []struct {
name string
key string
length int
}{
{name: "2I", key: MLI2I, length: 43},
{name: "2E", key: MLI2E, length: 43},
{name: "4I", key: MLI4I, length: 49},
{name: "4E", key: MLI4E, length: 34},
{name: "2EE", key: MLI2EE, length: 56},
{name: "2BCD2", key: MLI2BCD2, length: 284},
{name: "A4E", key: MLIA4E, length: 43},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var buf bytes.Buffer
err := EncodeWriter(&buf, tt.key, tt.length)
if err != nil {
t.Fatalf("EncodeWriter(%q, %d) unexpected error: %v", tt.key, tt.length, err)
}

expected, err := Encode(tt.key, tt.length)
if err != nil {
t.Fatalf("Encode(%q, %d) unexpected error: %v", tt.key, tt.length, err)
}

if !bytes.Equal(buf.Bytes(), expected) {
t.Errorf("EncodeWriter output mismatch: got %x, want %x", buf.Bytes(), expected)
}
})
}
}

func TestEncodeWriterErrors(t *testing.T) {
t.Run("Invalid key", func(t *testing.T) {
var buf bytes.Buffer
err := EncodeWriter(&buf, "Invalid", 10)
if err == nil {
t.Error("Expected error for invalid key")
}
})

t.Run("Negative length", func(t *testing.T) {
var buf bytes.Buffer
err := EncodeWriter(&buf, MLI2I, -1)
if !errors.Is(err, ErrLength) {
t.Errorf("Expected ErrLength, got %v", err)
}
})
}

func TestDecodeReader(t *testing.T) {
tests := []struct {
name string
key string
length int
}{
{name: "2I", key: MLI2I, length: 43},
{name: "2E", key: MLI2E, length: 43},
{name: "4I", key: MLI4I, length: 49},
{name: "4E", key: MLI4E, length: 34},
{name: "2EE", key: MLI2EE, length: 56},
{name: "2BCD2", key: MLI2BCD2, length: 284},
{name: "A4E", key: MLIA4E, length: 43},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
encoded, err := Encode(tt.key, tt.length)
if err != nil {
t.Fatalf("Encode(%q, %d) unexpected error: %v", tt.key, tt.length, err)
}

reader := bytes.NewReader(encoded)
got, err := DecodeReader(reader, tt.key)
if err != nil {
t.Fatalf("DecodeReader(%q) unexpected error: %v", tt.key, err)
}

if got != tt.length {
t.Errorf("DecodeReader(%q) = %d, want %d", tt.key, got, tt.length)
}
})
}
}

func TestDecodeReaderErrors(t *testing.T) {
t.Run("Invalid key", func(t *testing.T) {
reader := bytes.NewReader([]byte{0x00, 0x00})
_, err := DecodeReader(reader, "Invalid")
if err == nil {
t.Error("Expected error for invalid key")
}
})

t.Run("Insufficient bytes", func(t *testing.T) {
reader := bytes.NewReader([]byte{0x00}) // Only 1 byte for 2I which needs 2
_, err := DecodeReader(reader, MLI2I)
if !errors.Is(err, ErrByteSize) {
t.Errorf("Expected ErrByteSize, got %v", err)
}
})

t.Run("EOF", func(t *testing.T) {
reader := bytes.NewReader([]byte{})
_, err := DecodeReader(reader, MLI2I)
if !errors.Is(err, ErrByteSize) {
t.Errorf("Expected ErrByteSize, got %v", err)
}
})
}