Browse Source

A tested first version of zmostp-go.

ZRY 2 months ago
parent
commit
5362f99ef4

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/tests/dist/

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 7 - 0
.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="XMakeProjectSettings">
+    <option name="currentArchitecture" value="x86" />
+    <option name="workingDirectory" value="$PROJECT_DIR$" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/zmostp.iml" filepath="$PROJECT_DIR$/.idea/zmostp.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 11 - 0
.idea/zmostp.iml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <excludeFolder url="file://$MODULE_DIR$/tests/dist" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 7 - 0
go.work

@@ -0,0 +1,7 @@
+
+use zmostp-go
+use tests/test1
+use tests/test2
+use tests/test3
+use tests/echo-server
+use tests/re-enc-echo-server

+ 11 - 0
justfile

@@ -0,0 +1,11 @@
+#!/usr/bin/env just --justfile
+jfdir := replace(justfile_directory(), "\\", "/")
+tests_dir := jfdir / "tests"
+tests_dist := tests_dir / "dist"
+
+
+default:
+	just --list
+
+build_testsing_server name:
+	cd {{tests_dir / name }}; go build -o {{ tests_dist / name + ".exe" }}

+ 1 - 0
tests/echo-server/go.mod

@@ -0,0 +1 @@
+module git.swzry.com/zry/zmostp/tests/echo-server

+ 68 - 0
tests/echo-server/main.go

@@ -0,0 +1,68 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"net"
+)
+
+var flagHelp bool
+var flagEcho bool
+
+func main() {
+	flag.BoolVar(&flagHelp, "h", false, "print help")
+	flag.BoolVar(&flagEcho, "e", false, "echo (send back what received)")
+	flag.Parse()
+	if flagHelp {
+		flag.PrintDefaults()
+		return
+	}
+	ln, err := net.Listen("tcp", ":12450")
+	if err != nil {
+		fmt.Println("Error listening:", err.Error())
+		return
+	}
+	defer ln.Close()
+
+	fmt.Println("Listening on :12450")
+
+	for {
+		// 接受连接
+		conn, err := ln.Accept()
+		if err != nil {
+			fmt.Println("Error accepting connection:", err.Error())
+			return
+		}
+		fmt.Println("Client connected:", conn.RemoteAddr())
+
+		// 在新的goroutine中处理连接
+		go handleConnection(conn)
+	}
+}
+
+func handleConnection(conn net.Conn) {
+	defer conn.Close()
+
+	// 循环读取数据并回显
+	for {
+		buf := make([]byte, 1024)
+		// 读取数据
+		n, err := conn.Read(buf)
+		if err != nil {
+			fmt.Println("Error reading:", err.Error())
+			return
+		}
+
+		// 打印接收到的数据
+		fmt.Println("Received:\n", string(buf[:n]))
+
+		// 发送回去
+		if flagEcho {
+			_, err = conn.Write(buf[:n])
+			if err != nil {
+				fmt.Println("Error writing:", err.Error())
+				return
+			}
+		}
+	}
+}

+ 1 - 0
tests/re-enc-echo-server/go.mod

@@ -0,0 +1 @@
+module git.swzry.com/zry/zmostp/tests/re-enc-echo-server

+ 47 - 0
tests/re-enc-echo-server/log_hdl.go

@@ -0,0 +1,47 @@
+package main
+
+import (
+	"fmt"
+	zmostp_go "git.swzry.com/zry/zmostp/zmostp-go"
+)
+
+type LogHandler struct {
+}
+
+func (l LogHandler) DFAInvalidState(st zmostp_go.DFAState) {
+	fmt.Printf("[WRN] invalid DFA state: %d\n", st)
+}
+
+func (l LogHandler) DFAInvalidInput(st zmostp_go.DFAState, b byte) {
+	fmt.Printf("[WRN] invalid input '%d' on DFA state '%s'\n", b, st.ToAbbr())
+}
+
+func (l LogHandler) DecodeChannelAscii85Failed(data []byte, err error) {
+	fmt.Printf("[WRN] failed decoding channel data '%s' with ascii-85 encoding: %v\n", string(data), err)
+}
+
+func (l LogHandler) DecodePayloadAscii85Failed(ch uint32, data []byte, err error) {
+	fmt.Printf("[WRN] failed decoding payload data '%s' on ch %d with ascii-85 encoding: %v\n", string(data), ch, err)
+}
+
+func (l LogHandler) DecodeChecksumAscii85Failed(ch uint32, data []byte, err error) {
+	fmt.Printf("[WRN] failed decoding checksum data '%s' on ch %d with ascii-85 encoding: %v\n", string(data), ch, err)
+}
+
+func (l LogHandler) NeedCRCChecksum() {
+	fmt.Println("[WRN] need CRC checksum")
+}
+
+func (l LogHandler) CRCChecksumMismached(crcFromData uint32, crcReceived uint32) {
+	fmt.Printf("[WRN] CRC checksum mismatch: want %d, got %d\n", crcFromData, crcReceived)
+}
+
+func (l LogHandler) InvalidChannelByteLength(data []byte) {
+	fmt.Printf("[WRN] invalid channel byte length, data: %v\n", data)
+}
+
+func (l LogHandler) InvalidChecksumByteLength(ch uint32, data []byte) {
+	fmt.Printf("[WRN] invalid checksum byte length,ch %d, data: %v\n", ch, data)
+}
+
+var _ zmostp_go.DecodeFailureLogHandler = (*LogHandler)(nil)

+ 97 - 0
tests/re-enc-echo-server/main.go

@@ -0,0 +1,97 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	zmostp_go "git.swzry.com/zry/zmostp/zmostp-go"
+	"net"
+	"sync"
+)
+
+var flagHelp bool
+var flagBind string
+var LH *LogHandler
+
+func main() {
+	flag.BoolVar(&flagHelp, "h", false, "print help")
+	flag.StringVar(&flagBind, "b", ":19810", "bind address")
+	flag.Parse()
+	if flagHelp {
+		flag.PrintDefaults()
+		return
+	}
+	LH = &LogHandler{}
+	ln, err := net.Listen("tcp", flagBind)
+	if err != nil {
+		fmt.Println("Error listening:", err.Error())
+		return
+	}
+	defer ln.Close()
+
+	fmt.Println("Listening on ", flagBind)
+
+	for {
+		conn, err := ln.Accept()
+		if err != nil {
+			fmt.Println("Error accepting connection:", err.Error())
+			return
+		}
+		fmt.Println("Client connected:", conn.RemoteAddr())
+
+		go handleConnection(conn)
+	}
+}
+
+func handleConnection(conn net.Conn) {
+	defer conn.Close()
+	dec := zmostp_go.NewReceiver(&zmostp_go.ReceiverConfig{
+		ByteReader:       zmostp_go.NewSimpleByteReader(conn),
+		EnableCRC:        true,
+		DecodeFailureLog: LH,
+	})
+	enc := zmostp_go.NewTransmitter(&zmostp_go.TransmitterConfig{
+		Writer:               conn,
+		HumanFriendlySending: false,
+		EnableCRC:            true,
+	})
+	inch := enc.GetMessageInChannel()
+	outch := dec.GetMessageOutChannel()
+	wg := sync.WaitGroup{}
+	wg.Add(3)
+	ctx, cncl := context.WithCancel(context.Background())
+	go func() {
+		err := enc.Run(ctx)
+		if err != nil {
+			fmt.Printf("[ERR] error in encoder of conn '%s': %v\n", conn.RemoteAddr(), err)
+		} else {
+			fmt.Printf("[LOG] encoder of conn '%s' done\n", conn.RemoteAddr())
+		}
+		cncl()
+		wg.Done()
+	}()
+	go func() {
+		err := dec.Run(ctx)
+		if err != nil {
+			fmt.Printf("[ERR] error in decoder of conn '%s': %v\n", conn.RemoteAddr(), err)
+		} else {
+			fmt.Printf("[LOG] decoder of conn '%s' done\n", conn.RemoteAddr())
+		}
+		cncl()
+		wg.Done()
+	}()
+	go func() {
+		for {
+			select {
+			case msg := <-outch:
+				fmt.Printf("[MSG] Recv msg from ch %d: %v\n", msg.Channel, string(msg.Payload))
+				inch <- msg
+			case <-ctx.Done():
+				fmt.Printf("[LOG] logic of conn '%s' done\n", conn.RemoteAddr())
+				return
+			}
+		}
+	}()
+	wg.Wait()
+	cncl()
+}

+ 1 - 0
tests/test1/go.mod

@@ -0,0 +1 @@
+module git.swzry.com/zry/zmostp/tests/test1

+ 56 - 0
tests/test1/log_hdl.go

@@ -0,0 +1,56 @@
+package test1
+
+import (
+	"fmt"
+	zmostp_go "git.swzry.com/zry/zmostp/zmostp-go"
+)
+
+type LogHandler struct {
+}
+
+func (l LogHandler) InvalidChannelByteLength(data []byte) {
+	perr := fmt.Errorf("invalid channel byte length, data: %v", data)
+	panic(perr)
+}
+
+func (l LogHandler) InvalidChecksumByteLength(ch uint32, data []byte) {
+	perr := fmt.Errorf("invalid checksum byte length, ch: %d, data: %v", ch, data)
+	panic(perr)
+}
+
+func (l LogHandler) DecodeChecksumAscii85Failed(ch uint32, data []byte, err error) {
+	perr := fmt.Errorf("failed decoding checksum '%s' with ascii85 (ch=%d): %v", string(data), ch, err)
+	panic(perr)
+}
+
+func (l LogHandler) NeedCRCChecksum() {
+	perr := fmt.Errorf("crc checksum enabled but received data has no crc checksum")
+	panic(perr)
+}
+
+func (l LogHandler) CRCChecksumMismached(crcFromData uint32, crcReceived uint32) {
+	perr := fmt.Errorf("crc mismatch: calculated from data: %d, received: %d", crcFromData, crcReceived)
+	panic(perr)
+}
+
+func (l LogHandler) DFAInvalidState(st zmostp_go.DFAState) {
+	perr := fmt.Errorf("invalid dfa state: %d", st)
+	panic(perr)
+}
+
+func (l LogHandler) DFAInvalidInput(st zmostp_go.DFAState, b byte) {
+	perr := fmt.Errorf("invalid input: %d in state '%s' ", b, st.ToAbbr())
+	panic(perr)
+}
+
+func (l LogHandler) DecodeChannelAscii85Failed(data []byte, err error) {
+	perr := fmt.Errorf("failed decoding channel '%s' with ascii85: %v", string(data), err)
+	panic(perr)
+}
+
+func (l LogHandler) DecodePayloadAscii85Failed(ch uint32, data []byte, err error) {
+	perr := fmt.Errorf("failed decoding payload '%s' with ascii85 (ch=%d): %v", string(data), ch, err)
+	panic(perr)
+}
+
+var _ zmostp_go.DecodeFailureLogHandler = (*LogHandler)(nil)

+ 122 - 0
tests/test1/loop_test.go

@@ -0,0 +1,122 @@
+package test1
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	zmostp_go "git.swzry.com/zry/zmostp/zmostp-go"
+	"io"
+	"os"
+	"sync"
+	"testing"
+)
+
+type TestCase struct {
+	Channel uint32
+	Payload []byte
+}
+
+var ENC *zmostp_go.Transmitter
+var DEC *zmostp_go.Receiver
+var PIPE_W *io.PipeWriter
+var PIPE_R *io.PipeReader
+var LH *LogHandler
+var INCH chan *zmostp_go.Message
+var OUTCH chan *zmostp_go.Message
+
+func TestMain(m *testing.M) {
+	pr, pw := io.Pipe()
+	PIPE_W = pw
+	PIPE_R = pr
+	LH = &LogHandler{}
+	ENC = zmostp_go.NewTransmitter(&zmostp_go.TransmitterConfig{
+		Writer:               PIPE_W,
+		HumanFriendlySending: false,
+		EnableCRC:            true,
+	})
+	DEC = zmostp_go.NewReceiver(&zmostp_go.ReceiverConfig{
+		ByteReader:       zmostp_go.NewSimpleByteReader(PIPE_R),
+		EnableCRC:        true,
+		DecodeFailureLog: LH,
+	})
+	INCH = ENC.GetMessageInChannel()
+	OUTCH = DEC.GetMessageOutChannel()
+	wg := sync.WaitGroup{}
+	wg.Add(2)
+	ctx, cncl := context.WithCancel(context.Background())
+	go func() {
+		err := ENC.Run(ctx)
+		if err != nil {
+			panic(err)
+		}
+		wg.Done()
+	}()
+	go func() {
+		err := DEC.Run(ctx)
+		if err != nil {
+			panic(err)
+		}
+		wg.Done()
+	}()
+	fmt.Println("==== Init Ok ====")
+	exitCode := m.Run()
+	fmt.Println("==== Test End ====")
+	cncl()
+	_ = PIPE_W.Close()
+	_ = PIPE_R.Close()
+	fmt.Println("==== Waiting for exit ====")
+	wg.Wait()
+	os.Exit(exitCode)
+}
+
+func FuzzLooping(f *testing.F) {
+	f.Log(" ==== fuzz looping test ====")
+	testcases := []TestCase{
+		{0, []byte("hello")},
+		{114514, []byte("hello, gensoukyo")},
+		{0xAA550101, []byte{0xAA, 0xA5, 0x5A, 0x01, 0x55, 0x00}},
+	}
+	for _, tci := range testcases {
+		f.Add(tci.Channel, tci.Payload)
+	}
+	f.Fuzz(func(t *testing.T, ch uint32, payload []byte) {
+		INCH <- &zmostp_go.Message{
+			Channel: ch,
+			Payload: payload,
+		}
+		o := <-OUTCH
+		if o.Channel != ch {
+			t.Errorf("got channel: %d, want: %d", o.Channel, ch)
+		}
+		if !bytes.Equal(o.Payload, payload) {
+			t.Errorf("got payload: %s, want: %s", o.Payload, payload)
+		}
+	})
+}
+
+func FuzzLoopingWithHumanFriendlySwitching(f *testing.F) {
+	f.Log(" ==== fuzz looping test ====")
+	testcases := []TestCase{
+		{0, []byte("hello")},
+		{114514, []byte("hello, gensoukyo")},
+		{0xAA550101, []byte{0xAA, 0xA5, 0x5A, 0x01, 0x55, 0x00}},
+	}
+	for _, tci := range testcases {
+		f.Add(tci.Channel, tci.Payload, true)
+		f.Add(tci.Channel, tci.Payload, false)
+	}
+	f.Fuzz(func(t *testing.T, ch uint32, payload []byte, hf bool) {
+		ENC.SetHumanFriendOutputOption(hf)
+		INCH <- &zmostp_go.Message{
+			Channel: ch,
+			Payload: payload,
+		}
+		o := <-OUTCH
+		if o.Channel != ch {
+			t.Errorf("got channel: %d, want: %d", o.Channel, ch)
+		}
+		if !bytes.Equal(o.Payload, payload) {
+			t.Errorf("got payload: %s, want: %s", o.Payload, payload)
+		}
+	})
+}

+ 1 - 0
tests/test2/go.mod

@@ -0,0 +1 @@
+module git.swzry.com/zry/zmostp/tests/test2

+ 74 - 0
tests/test2/send_test.go

@@ -0,0 +1,74 @@
+package test2
+
+import (
+	"context"
+	"fmt"
+	zmostp_go "git.swzry.com/zry/zmostp/zmostp-go"
+	"net"
+	"os"
+	"sync"
+	"testing"
+)
+
+type TestCase struct {
+	Channel uint32
+	Payload []byte
+}
+
+var ENC *zmostp_go.Transmitter
+var INCH chan *zmostp_go.Message
+
+func TestMain(m *testing.M) {
+	addr, err := net.ResolveTCPAddr("tcp", "localhost:19810")
+	if err != nil {
+		panic(fmt.Errorf("failed resolving tcp address: %v", err))
+	}
+	conn, err := net.DialTCP("tcp", nil, addr)
+	if err != nil {
+		panic(fmt.Errorf("failed dialing tcp: %v", err))
+	}
+	ENC = zmostp_go.NewTransmitter(&zmostp_go.TransmitterConfig{
+		Writer:               conn,
+		HumanFriendlySending: false,
+		EnableCRC:            true,
+	})
+	INCH = ENC.GetMessageInChannel()
+	wg := sync.WaitGroup{}
+	wg.Add(1)
+	ctx, cncl := context.WithCancel(context.Background())
+	go func() {
+		err := ENC.Run(ctx)
+		if err != nil {
+			panic(err)
+		}
+		wg.Done()
+	}()
+	fmt.Println("==== Init Ok ====")
+	exitCode := m.Run()
+	fmt.Println("==== Test End ====")
+	cncl()
+	_ = conn.Close()
+	fmt.Println("==== Waiting for exit ====")
+	wg.Wait()
+	os.Exit(exitCode)
+}
+
+func FuzzSendWithHumanFriendlySwitching(f *testing.F) {
+	f.Log(" ==== fuzz looping test ====")
+	testcases := []TestCase{
+		{0, []byte("hello")},
+		{114514, []byte("hello, gensoukyo")},
+		{0xAA550101, []byte{0xAA, 0xA5, 0x5A, 0x01, 0x55, 0x00}},
+	}
+	for _, tci := range testcases {
+		f.Add(tci.Channel, tci.Payload, true)
+		f.Add(tci.Channel, tci.Payload, false)
+	}
+	f.Fuzz(func(t *testing.T, ch uint32, payload []byte, hf bool) {
+		ENC.SetHumanFriendOutputOption(hf)
+		INCH <- &zmostp_go.Message{
+			Channel: ch,
+			Payload: payload,
+		}
+	})
+}

+ 1 - 0
tests/test3/go.mod

@@ -0,0 +1 @@
+module git.swzry.com/zry/zmostp/tests/test3

+ 56 - 0
tests/test3/log_hdl.go

@@ -0,0 +1,56 @@
+package test3
+
+import (
+	"fmt"
+	zmostp_go "git.swzry.com/zry/zmostp/zmostp-go"
+)
+
+type LogHandler struct {
+}
+
+func (l LogHandler) DecodeChecksumAscii85Failed(ch uint32, data []byte, err error) {
+	perr := fmt.Errorf("failed decoding checksum '%s' with ascii85 (ch=%d): %v", string(data), ch, err)
+	panic(perr)
+}
+
+func (l LogHandler) NeedCRCChecksum() {
+	perr := fmt.Errorf("crc checksum enabled but received data has no crc checksum")
+	panic(perr)
+}
+
+func (l LogHandler) CRCChecksumMismached(crcFromData uint32, crcReceived uint32) {
+	perr := fmt.Errorf("crc mismatch: calculated from data: %d, received: %d", crcFromData, crcReceived)
+	panic(perr)
+}
+
+func (l LogHandler) DFAInvalidState(st zmostp_go.DFAState) {
+	perr := fmt.Errorf("invalid dfa state: %d", st)
+	panic(perr)
+}
+
+func (l LogHandler) DFAInvalidInput(st zmostp_go.DFAState, b byte) {
+	perr := fmt.Errorf("invalid input: %d in state '%s' ", b, st.ToAbbr())
+	panic(perr)
+}
+
+func (l LogHandler) DecodeChannelAscii85Failed(data []byte, err error) {
+	perr := fmt.Errorf("failed decoding channel '%s' with ascii85: %v", string(data), err)
+	panic(perr)
+}
+
+func (l LogHandler) DecodePayloadAscii85Failed(ch uint32, data []byte, err error) {
+	perr := fmt.Errorf("failed decoding payload '%s' with ascii85 (ch=%d): %v", string(data), ch, err)
+	panic(perr)
+}
+
+func (l LogHandler) InvalidChannelByteLength(data []byte) {
+	perr := fmt.Errorf("invalid channel byte length, data: %v", data)
+	panic(perr)
+}
+
+func (l LogHandler) InvalidChecksumByteLength(ch uint32, data []byte) {
+	perr := fmt.Errorf("invalid checksum byte length, ch: %d, data: %v", ch, data)
+	panic(perr)
+}
+
+var _ zmostp_go.DecodeFailureLogHandler = (*LogHandler)(nil)

+ 137 - 0
tests/test3/re_enc_svr_client_test.go

@@ -0,0 +1,137 @@
+package test3
+
+import (
+	"bytes"
+	"context"
+	"flag"
+	"fmt"
+	zmostp_go "git.swzry.com/zry/zmostp/zmostp-go"
+	"math/rand"
+	"net"
+	"os"
+	"sync"
+	"testing"
+)
+
+type TestCase struct {
+	Channel uint32
+	Payload []byte
+}
+
+var flagHelp bool
+var flagServer string
+var flagBatchTestCount int
+var flagMaxRandomDataLength int
+
+var ENC *zmostp_go.Transmitter
+var DEC *zmostp_go.Receiver
+var INCH chan *zmostp_go.Message
+var OUTCH chan *zmostp_go.Message
+var LH *LogHandler
+
+func TestMain(m *testing.M) {
+	fmt.Println("start.")
+	flag.BoolVar(&flagHelp, "h", false, "print help")
+	flag.StringVar(&flagServer, "s", "localhost:19810", "server address")
+	flag.IntVar(&flagBatchTestCount, "b", 100, "batch test count")
+	flag.IntVar(&flagMaxRandomDataLength, "l", 64, "max random data length")
+	flag.Parse()
+	if flagHelp {
+		flag.PrintDefaults()
+		return
+	}
+	addr, err := net.ResolveTCPAddr("tcp", flagServer)
+	if err != nil {
+		panic(fmt.Errorf("failed resolving tcp address: %v", err))
+	}
+	conn, err := net.DialTCP("tcp", nil, addr)
+	if err != nil {
+		panic(fmt.Errorf("failed dialing tcp: %v", err))
+	}
+	LH = &LogHandler{}
+	ENC = zmostp_go.NewTransmitter(&zmostp_go.TransmitterConfig{
+		Writer:               conn,
+		HumanFriendlySending: false,
+		EnableCRC:            true,
+	})
+	DEC = zmostp_go.NewReceiver(&zmostp_go.ReceiverConfig{
+		ByteReader:       zmostp_go.NewSimpleByteReader(conn),
+		EnableCRC:        true,
+		DecodeFailureLog: LH,
+	})
+	INCH = ENC.GetMessageInChannel()
+	OUTCH = DEC.GetMessageOutChannel()
+	wg := sync.WaitGroup{}
+	wg.Add(2)
+	ctx, cncl := context.WithCancel(context.Background())
+	go func() {
+		err := ENC.Run(ctx)
+		if err != nil {
+			panic(err)
+		}
+		wg.Done()
+	}()
+	go func() {
+		err := DEC.Run(ctx)
+		if err != nil {
+			panic(err)
+		}
+		wg.Done()
+	}()
+	fmt.Println("==== Init Ok ====")
+	exitCode := m.Run()
+	fmt.Println("==== Test End ====")
+	cncl()
+	_ = conn.Close()
+	fmt.Println("==== Waiting for exit ====")
+	wg.Wait()
+	os.Exit(exitCode)
+}
+
+func TestFixedTestCase(t *testing.T) {
+	testcases := []TestCase{
+		{0, []byte("hello")},
+		{114514, []byte("hello, gensoukyo")},
+		{0xAA550101, []byte{0xAA, 0xA5, 0x5A, 0x01, 0x55, 0x00}},
+	}
+	for _, v := range testcases {
+		testOneData(t, v.Channel, v.Payload, false)
+		testOneData(t, v.Channel, v.Payload, true)
+	}
+}
+
+func testOneData(t *testing.T, ch uint32, payload []byte, hf bool) {
+	ENC.SetHumanFriendOutputOption(hf)
+	INCH <- &zmostp_go.Message{
+		Channel: ch,
+		Payload: payload,
+	}
+	t.Logf("wait on ch %d", ch)
+	rep := <-OUTCH
+	if rep.Channel != ch {
+		t.Fatalf("channel mismatch: got %d, want %d", rep.Channel, ch)
+	}
+	if !bytes.Equal(rep.Payload, payload) {
+		t.Fatalf("payload mismatch: got %s, want %s", rep.Payload, payload)
+	}
+	t.Logf("test on ch %d ok", ch)
+}
+
+func pickRandom() *TestCase {
+	ch := rand.Uint32()
+	payloadLen := rand.Intn(flagMaxRandomDataLength)
+	payload := make([]byte, payloadLen)
+	rand.Read(payload)
+	return &TestCase{
+		Channel: ch,
+		Payload: payload,
+	}
+}
+
+func TestRandomTestCase(t *testing.T) {
+	for i := 0; i < flagBatchTestCount; i++ {
+		tc := pickRandom()
+		testOneData(t, tc.Channel, tc.Payload, false)
+		testOneData(t, tc.Channel, tc.Payload, true)
+	}
+}

+ 15 - 0
zmostp-go/cfg.go

@@ -0,0 +1,15 @@
+package zmostp_go
+
+import "io"
+
+type TransmitterConfig struct {
+	Writer               io.Writer
+	HumanFriendlySending bool
+	EnableCRC            bool
+}
+
+type ReceiverConfig struct {
+	ByteReader       io.ByteReader
+	EnableCRC        bool
+	DecodeFailureLog DecodeFailureLogHandler
+}

+ 136 - 0
zmostp-go/dfa.go

@@ -0,0 +1,136 @@
+package zmostp_go
+
+type DFAOutMsg struct {
+	RawChannel []byte
+	RawPayload []byte
+	Checksum   []byte
+}
+
+type DFAState uint8
+
+const (
+	DFAS_IDLE             DFAState = iota
+	DFAS_HEADER_RECEIVED  DFAState = iota
+	DFAS_CHNANEL_RECEIVED DFAState = iota
+	DFAS_PAYLOAD_RECEIVED DFAState = iota
+)
+
+func (s DFAState) ToAbbr() string {
+	switch s {
+	case DFAS_IDLE:
+		return "IDLE"
+	case DFAS_HEADER_RECEIVED:
+		return "HDRC"
+	case DFAS_CHNANEL_RECEIVED:
+		return "CHRC"
+	case DFAS_PAYLOAD_RECEIVED:
+		return "PDRC"
+	default:
+		return "UKNW"
+	}
+}
+
+type DecoderDFA struct {
+	outCh  chan *DFAOutMsg
+	st     DFAState
+	flh    DecodeFailureLogHandler
+	chnbuf []byte
+	pdbuf  []byte
+	crcbuf []byte
+}
+
+func NewDecoderDFA(flh DecodeFailureLogHandler) *DecoderDFA {
+	a := &DecoderDFA{
+		outCh: make(chan *DFAOutMsg),
+		st:    DFAS_IDLE,
+		flh:   flh,
+	}
+	return a
+}
+
+func (a *DecoderDFA) GetOutChannel() chan *DFAOutMsg {
+	return a.outCh
+}
+
+func (a *DecoderDFA) Shutdown() {
+	close(a.outCh)
+}
+
+func (a *DecoderDFA) Reset() {
+	close(a.outCh)
+	a.st = DFAS_IDLE
+	a.outCh = make(chan *DFAOutMsg)
+}
+
+func (a *DecoderDFA) PushByte(b byte) {
+	switch a.st {
+	default:
+		a.flh.DFAInvalidState(a.st)
+		a.st = DFAS_IDLE
+	case DFAS_IDLE:
+		switch b {
+		default:
+			a.flh.DFAInvalidInput(a.st, b)
+			a.st = DFAS_IDLE
+		case '{':
+			a.chnbuf = make([]byte, 0, 5)
+			a.pdbuf = make([]byte, 0)
+			a.crcbuf = make([]byte, 0, 5)
+			a.st = DFAS_HEADER_RECEIVED
+		case '\t', ' ', '\r', '\n':
+			// ignore whitespace
+		}
+	case DFAS_HEADER_RECEIVED:
+		switch {
+		case b >= '!' && b <= 'u' || b == 'z':
+			a.chnbuf = append(a.chnbuf, b)
+		case b == '|':
+			a.st = DFAS_CHNANEL_RECEIVED
+		case b == '\t' || b == ' ' || b == '\r' || b == '\n':
+			// ignore whitespace
+		default:
+			a.flh.DFAInvalidInput(a.st, b)
+			a.st = DFAS_IDLE
+		}
+	case DFAS_CHNANEL_RECEIVED:
+		switch {
+		case b >= '!' && b <= 'u' || b == 'z':
+			a.pdbuf = append(a.pdbuf, b)
+		case b == '|':
+			a.st = DFAS_PAYLOAD_RECEIVED
+		case b == '}':
+			go func() {
+				a.outCh <- &DFAOutMsg{
+					RawChannel: a.chnbuf,
+					RawPayload: a.pdbuf,
+					Checksum:   []byte{},
+				}
+			}()
+			a.st = DFAS_IDLE
+		case b == '\t' || b == ' ' || b == '\r' || b == '\n':
+			// ignore whitespace
+		default:
+			a.flh.DFAInvalidInput(a.st, b)
+			a.st = DFAS_IDLE
+		}
+	case DFAS_PAYLOAD_RECEIVED:
+		switch {
+		case b >= '!' && b <= 'u' || b == 'z':
+			a.crcbuf = append(a.crcbuf, b)
+		case b == '}':
+			go func() {
+				a.outCh <- &DFAOutMsg{
+					RawChannel: a.chnbuf,
+					RawPayload: a.pdbuf,
+					Checksum:   a.crcbuf,
+				}
+			}()
+			a.st = DFAS_IDLE
+		case b == '\t' || b == ' ' || b == '\r' || b == '\n':
+			// ignore whitespace
+		default:
+			a.flh.DFAInvalidInput(a.st, b)
+			a.st = DFAS_IDLE
+		}
+	}
+}

+ 44 - 0
zmostp-go/failure_log.go

@@ -0,0 +1,44 @@
+package zmostp_go
+
+type DecodeFailureLogHandler interface {
+	DFAInvalidState(st DFAState)
+	DFAInvalidInput(st DFAState, b byte)
+	DecodeChannelAscii85Failed(data []byte, err error)
+	DecodePayloadAscii85Failed(ch uint32, data []byte, err error)
+	DecodeChecksumAscii85Failed(ch uint32, data []byte, err error)
+	NeedCRCChecksum()
+	CRCChecksumMismached(crcFromData uint32, crcReceived uint32)
+	InvalidChannelByteLength(data []byte)
+	InvalidChecksumByteLength(ch uint32, data []byte)
+}
+
+type EmptyDecodeFailureLogHandler struct{}
+
+func (e EmptyDecodeFailureLogHandler) InvalidChannelByteLength(data []byte) {
+}
+
+func (e EmptyDecodeFailureLogHandler) InvalidChecksumByteLength(ch uint32, data []byte) {
+}
+
+func (e EmptyDecodeFailureLogHandler) CRCChecksumMismached(want uint32, got uint32) {
+}
+
+func (e EmptyDecodeFailureLogHandler) DecodeChecksumAscii85Failed(ch uint32, data []byte, err error) {
+}
+
+func (e EmptyDecodeFailureLogHandler) NeedCRCChecksum() {
+}
+
+func (e EmptyDecodeFailureLogHandler) DFAInvalidState(st DFAState) {
+}
+
+func (e EmptyDecodeFailureLogHandler) DecodePayloadAscii85Failed(ch uint32, data []byte, err error) {
+}
+
+func (e EmptyDecodeFailureLogHandler) DecodeChannelAscii85Failed(data []byte, err error) {
+}
+
+func (e EmptyDecodeFailureLogHandler) DFAInvalidInput(st DFAState, b byte) {
+}
+
+var _ DecodeFailureLogHandler = (*EmptyDecodeFailureLogHandler)(nil)

+ 1 - 0
zmostp-go/go.mod

@@ -0,0 +1 @@
+module git.swzry.com/zry/zmostp/zmostp-go

+ 6 - 0
zmostp-go/msg.go

@@ -0,0 +1,6 @@
+package zmostp_go
+
+type Message struct {
+	Channel uint32
+	Payload []byte
+}

+ 134 - 0
zmostp-go/receiver.go

@@ -0,0 +1,134 @@
+package zmostp_go
+
+import (
+	"bytes"
+	"context"
+	"encoding/ascii85"
+	"encoding/binary"
+	"fmt"
+	"hash/crc32"
+	"io"
+)
+
+type Receiver struct {
+	reader              io.ByteReader
+	outch               chan *Message
+	dfa                 *DecoderDFA
+	decodeFailureLogHdl DecodeFailureLogHandler
+	enableCRC           bool
+}
+
+func NewReceiver(cfg *ReceiverConfig) *Receiver {
+	t := &Receiver{
+		reader:    cfg.ByteReader,
+		outch:     make(chan *Message),
+		enableCRC: cfg.EnableCRC,
+	}
+	if cfg.DecodeFailureLog == nil {
+		t.decodeFailureLogHdl = &EmptyDecodeFailureLogHandler{}
+		t.dfa = NewDecoderDFA(&EmptyDecodeFailureLogHandler{})
+	} else {
+		t.decodeFailureLogHdl = cfg.DecodeFailureLog
+		t.dfa = NewDecoderDFA(cfg.DecodeFailureLog)
+	}
+	return t
+}
+
+func (t *Receiver) GetMessageOutChannel() chan *Message {
+	return t.outch
+}
+
+func (t *Receiver) Run(ctx context.Context) error {
+	recvErrCh := make(chan error)
+	recvByteCh := make(chan byte)
+	dfaOutCh := t.dfa.GetOutChannel()
+	go func() {
+		for {
+			b, err := t.reader.ReadByte()
+			if err != nil {
+				if err == io.EOF {
+					recvErrCh <- nil
+					return
+				} else {
+					recvErrCh <- err
+					return
+				}
+			}
+			recvByteCh <- b
+		}
+	}()
+	for {
+		select {
+		case <-ctx.Done():
+			close(recvErrCh)
+			close(recvByteCh)
+			close(t.outch)
+			t.dfa.Shutdown()
+			return nil
+		case e, ok := <-recvErrCh:
+			if !ok {
+				return nil
+			}
+			return fmt.Errorf("failed recv from stream: %w", e)
+		case b, ok := <-recvByteCh:
+			if !ok {
+				return nil
+			}
+			t.dfa.PushByte(b)
+		case dom, ok := <-dfaOutCh:
+			if !ok {
+				return nil
+			}
+			t.decodeMessageFromDFAOut(dom)
+		}
+	}
+}
+func (t *Receiver) decodeMessageFromDFAOut(msg *DFAOutMsg) {
+	decChannel := ascii85.NewDecoder(bytes.NewReader(msg.RawChannel))
+	decPayload := ascii85.NewDecoder(bytes.NewReader(msg.RawPayload))
+	chbyte, err := io.ReadAll(decChannel)
+	if err != nil {
+		t.decodeFailureLogHdl.DecodeChannelAscii85Failed(msg.RawChannel, err)
+		return
+	}
+	if len(chbyte) != 4 {
+		t.decodeFailureLogHdl.InvalidChannelByteLength(chbyte)
+		return
+	}
+	chnum := binary.LittleEndian.Uint32(chbyte)
+	payload, err := io.ReadAll(decPayload)
+	if err != nil {
+		t.decodeFailureLogHdl.DecodePayloadAscii85Failed(chnum, msg.RawPayload, err)
+		return
+	}
+	if t.enableCRC {
+		if len(msg.Checksum) <= 0 {
+			t.decodeFailureLogHdl.NeedCRCChecksum()
+			return
+		}
+		decCRC := ascii85.NewDecoder(bytes.NewReader(msg.Checksum))
+		crcBytes, err := io.ReadAll(decCRC)
+		if err != nil {
+			t.decodeFailureLogHdl.DecodeChecksumAscii85Failed(chnum, msg.RawPayload, err)
+			return
+		}
+		if len(crcBytes) != 4 {
+			t.decodeFailureLogHdl.InvalidChecksumByteLength(chnum, crcBytes)
+			return
+		}
+		crcNum := binary.LittleEndian.Uint32(crcBytes)
+		crcABytes := make([]byte, 0, len(payload)+4)
+		crcABytes = append(crcABytes, chbyte...)
+		crcABytes = append(crcABytes, payload...)
+		crcAChecksum := crc32.ChecksumIEEE(crcABytes)
+		if crcNum != crcAChecksum {
+			t.decodeFailureLogHdl.CRCChecksumMismached(crcAChecksum, crcNum)
+			return
+		}
+	}
+	omsg := &Message{
+		Channel: chnum,
+		Payload: payload,
+	}
+	t.outch <- omsg
+}

+ 29 - 0
zmostp-go/simple_byte_reader.go

@@ -0,0 +1,29 @@
+package zmostp_go
+
+import "io"
+
+var _ io.ByteReader = (*SimpleByteReader)(nil)
+
+// SimpleByteReader implements io.ByteReader for io.Reader.
+// Not recommended for production use due to performance issues.
+type SimpleByteReader struct {
+	reader io.Reader
+	buf    []byte
+}
+
+// NewSimpleByteReader creates a new SimpleByteReader.
+// Not recommended for production use due to performance issues.
+func NewSimpleByteReader(reader io.Reader) *SimpleByteReader {
+	return &SimpleByteReader{
+		reader: reader,
+		buf:    make([]byte, 1),
+	}
+}
+
+func (s SimpleByteReader) ReadByte() (byte, error) {
+	_, err := s.reader.Read(s.buf)
+	if err != nil {
+		return 0, err
+	}
+	return s.buf[0], nil
+}

+ 123 - 0
zmostp-go/transmitter.go

@@ -0,0 +1,123 @@
+package zmostp_go
+
+import (
+	"bytes"
+	"context"
+	"encoding/ascii85"
+	"encoding/binary"
+	"fmt"
+	"hash/crc32"
+	"io"
+	"sync"
+)
+
+type Transmitter struct {
+	writer               io.Writer
+	inch                 chan *Message
+	humanFriendlySending bool
+	optLock              sync.RWMutex
+	enableCRC            bool
+}
+
+func NewTransmitter(cfg *TransmitterConfig) *Transmitter {
+	t := &Transmitter{
+		writer:               cfg.Writer,
+		inch:                 make(chan *Message),
+		humanFriendlySending: cfg.HumanFriendlySending,
+		optLock:              sync.RWMutex{},
+		enableCRC:            cfg.EnableCRC,
+	}
+	return t
+}
+func (t *Transmitter) GetMessageInChannel() chan *Message {
+	return t.inch
+}
+
+func (t *Transmitter) SetHumanFriendOutputOption(b bool) {
+	defer t.optLock.Unlock()
+	t.optLock.Lock()
+	t.humanFriendlySending = b
+}
+
+func (t *Transmitter) Run(ctx context.Context) error {
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		case out, ok := <-t.inch:
+			if !ok {
+				return nil
+			}
+			err := t.encodeSendMessage(out, t.writer)
+			if err != nil {
+				return err
+			}
+		}
+	}
+}
+
+func (t *Transmitter) encodeSendMessage(msg *Message, w io.Writer) error {
+	defer t.optLock.RUnlock()
+	t.optLock.RLock()
+	var buf *bytes.Buffer
+	if t.humanFriendlySending {
+		buf = bytes.NewBufferString("{\n\t")
+	} else {
+		buf = bytes.NewBuffer([]byte{'{'})
+	}
+	asc85end := ascii85.NewEncoder(buf)
+	chnBytes := binary.LittleEndian.AppendUint32([]byte{}, msg.Channel)
+	_, err := asc85end.Write(chnBytes)
+	if err != nil {
+		return fmt.Errorf("failed to encode chnum of message: ascii85 encoder write error: %w", err)
+	}
+	err = asc85end.Close()
+	if err != nil {
+		return fmt.Errorf("failed to encode chnum of message: ascii85 encoder close error: %w", err)
+	}
+	if t.humanFriendlySending {
+		buf.WriteString("\n\t|\n\t")
+	} else {
+		buf.WriteByte('|')
+	}
+	asc85end = ascii85.NewEncoder(buf)
+	_, err = asc85end.Write(msg.Payload)
+	if err != nil {
+		return fmt.Errorf("failed to encode payload of message: ascii85 encoder write error: %w", err)
+	}
+	err = asc85end.Close()
+	if err != nil {
+		return fmt.Errorf("failed to encode payload of message: ascii85 encoder close error: %w", err)
+	}
+	if t.enableCRC {
+		if t.humanFriendlySending {
+			buf.WriteString("\n\t|\n\t")
+		} else {
+			buf.WriteByte('|')
+		}
+		crcBytes := make([]byte, 0, len(msg.Payload)+4)
+		crcBytes = append(crcBytes, chnBytes...)
+		crcBytes = append(crcBytes, msg.Payload...)
+		crcChecksum := crc32.ChecksumIEEE(crcBytes)
+		crcChsBytes := binary.LittleEndian.AppendUint32([]byte{}, crcChecksum)
+		asc85end = ascii85.NewEncoder(buf)
+		_, err = asc85end.Write(crcChsBytes)
+		if err != nil {
+			return fmt.Errorf("failed to encode checksum of message: ascii85 encoder write error: %w", err)
+		}
+		err = asc85end.Close()
+		if err != nil {
+			return fmt.Errorf("failed to encode checksum of message: ascii85 encoder close error: %w", err)
+		}
+	}
+	if t.humanFriendlySending {
+		buf.WriteString("\n}\n")
+	} else {
+		buf.WriteByte('}')
+	}
+	_, err = w.Write(buf.Bytes())
+	if err != nil {
+		return fmt.Errorf("failed to write message to stream: %w", err)
+	}
+	return nil
+}

+ 0 - 261
zmostp-go/zmostp_old.go

@@ -1,261 +0,0 @@
-package zmostp_go
-
-import (
-	"bytes"
-	"context"
-	"encoding/ascii85"
-	"encoding/binary"
-	"fmt"
-	"hash/crc32"
-	"io"
-	"sync"
-)
-
-type Transporter struct {
-	reader               io.ByteReader
-	writer               io.Writer
-	outch                chan *Message
-	inch                 chan *Message
-	humanFriendlySending bool
-	optLock              sync.RWMutex
-	dfa                  *DecoderDFA
-	decodeFailureLogHdl  DecodeFailureLogHandler
-	enableCRC            bool
-}
-
-func NewTransporter(cfg *TransporterConfig) *Transporter {
-	t := &Transporter{
-		reader:               cfg.Reader,
-		writer:               cfg.Writer,
-		outch:                make(chan *Message),
-		inch:                 make(chan *Message),
-		humanFriendlySending: cfg.HumanFriendlySending,
-		optLock:              sync.RWMutex{},
-		enableCRC:            cfg.EnableCRC,
-	}
-	if cfg.DecodeFailureLog == nil {
-		t.decodeFailureLogHdl = &EmptyDecodeFailureLogHandler{}
-		t.dfa = NewDecoderDFA(&EmptyDecodeFailureLogHandler{})
-	} else {
-		t.decodeFailureLogHdl = cfg.DecodeFailureLog
-		t.dfa = NewDecoderDFA(cfg.DecodeFailureLog)
-	}
-	return t
-}
-
-func (t *Transporter) GetMessageInChannel() chan *Message {
-	return t.inch
-}
-
-func (t *Transporter) GetMessageOutChannel() chan *Message {
-	return t.outch
-}
-
-func (t *Transporter) Run(ctx context.Context) error {
-	t.dfa.Reset()
-	var errSend, errRecv, errPipeCopy error
-	wg := &sync.WaitGroup{}
-	wg.Add(2)
-	pctx, cncl := context.WithCancel(ctx)
-	go func() {
-		errSend = t.doSend(pctx)
-		cncl()
-		wg.Done()
-	}()
-	go func() {
-		errRecv = t.doRecv(pctx)
-		cncl()
-		wg.Done()
-	}()
-	wg.Wait()
-	if errSend != nil {
-		return errSend
-	}
-	if errRecv != nil {
-		return errRecv
-	}
-	if errPipeCopy != nil {
-		if errPipeCopy == io.EOF {
-			return nil
-		}
-		return errPipeCopy
-	}
-	return nil
-}
-
-func (t *Transporter) doRecv(ctx context.Context) error {
-	recvErrCh := make(chan error)
-	recvByteCh := make(chan byte)
-	dfaOutCh := t.dfa.GetOutChannel()
-	go func() {
-		for {
-			b, err := t.reader.ReadByte()
-			if err != nil {
-				if err == io.EOF {
-					recvErrCh <- nil
-					return
-				} else {
-					recvErrCh <- err
-					return
-				}
-			}
-			recvByteCh <- b
-		}
-	}()
-	for {
-		select {
-		case <-ctx.Done():
-			close(recvErrCh)
-			close(recvByteCh)
-			close(t.outch)
-			t.dfa.Shutdown()
-			return nil
-		case e, ok := <-recvErrCh:
-			if !ok {
-				return nil
-			}
-			return fmt.Errorf("failed recv from stream: %w", e)
-		case b, ok := <-recvByteCh:
-			if !ok {
-				return nil
-			}
-			t.dfa.PushByte(b)
-		case dom, ok := <-dfaOutCh:
-			if !ok {
-				return nil
-			}
-			t.decodeMessageFromDFAOut(dom)
-		}
-	}
-}
-
-func (t *Transporter) doSend(ctx context.Context) error {
-	for {
-		select {
-		case <-ctx.Done():
-			return nil
-		case out, ok := <-t.inch:
-			if !ok {
-				return nil
-			}
-			err := t.encodeSendMessage(out, t.writer)
-			if err != nil {
-				return err
-			}
-		}
-	}
-}
-
-func (t *Transporter) SetHumanFriendOutputOption(b bool) {
-	defer t.optLock.Unlock()
-	t.optLock.Lock()
-	t.humanFriendlySending = b
-}
-
-func (t *Transporter) encodeSendMessage(msg *Message, w io.Writer) error {
-	defer t.optLock.RUnlock()
-	t.optLock.RLock()
-	var buf *bytes.Buffer
-	if t.humanFriendlySending {
-		buf = bytes.NewBufferString("{\n\t")
-	} else {
-		buf = bytes.NewBuffer([]byte{'{'})
-	}
-	asc85end := ascii85.NewEncoder(buf)
-	chnBytes := binary.LittleEndian.AppendUint32([]byte{}, msg.Channel)
-	_, err := asc85end.Write(chnBytes)
-	if err != nil {
-		return fmt.Errorf("failed to encode chnum of message: ascii85 encoder write error: %w", err)
-	}
-	err = asc85end.Close()
-	if err != nil {
-		return fmt.Errorf("failed to encode chnum of message: ascii85 encoder close error: %w", err)
-	}
-	if t.humanFriendlySending {
-		buf.WriteString("\n\t|\n\t")
-	} else {
-		buf.WriteByte('|')
-	}
-	asc85end = ascii85.NewEncoder(buf)
-	_, err = asc85end.Write(msg.Payload)
-	if err != nil {
-		return fmt.Errorf("failed to encode payload of message: ascii85 encoder write error: %w", err)
-	}
-	err = asc85end.Close()
-	if err != nil {
-		return fmt.Errorf("failed to encode payload of message: ascii85 encoder close error: %w", err)
-	}
-	if t.enableCRC {
-		if t.humanFriendlySending {
-			buf.WriteString("\n\t|\n\t")
-		} else {
-			buf.WriteByte('|')
-		}
-		crcBytes := make([]byte, 0, len(msg.Payload)+4)
-		crcBytes = append(crcBytes, chnBytes...)
-		crcBytes = append(crcBytes, msg.Payload...)
-		crcChecksum := crc32.ChecksumIEEE(crcBytes)
-		crcChsBytes := binary.LittleEndian.AppendUint32([]byte{}, crcChecksum)
-		asc85end = ascii85.NewEncoder(buf)
-		_, err = asc85end.Write(crcChsBytes)
-		if err != nil {
-			return fmt.Errorf("failed to encode checksum of message: ascii85 encoder write error: %w", err)
-		}
-		err = asc85end.Close()
-		if err != nil {
-			return fmt.Errorf("failed to encode checksum of message: ascii85 encoder close error: %w", err)
-		}
-	}
-	if t.humanFriendlySending {
-		buf.WriteString("\n}\n")
-	} else {
-		buf.WriteByte('}')
-	}
-	_, err = w.Write(buf.Bytes())
-	if err != nil {
-		return fmt.Errorf("failed to write message to stream: %w", err)
-	}
-	return nil
-}
-
-func (t *Transporter) decodeMessageFromDFAOut(msg *DFAOutMsg) {
-	decChannel := ascii85.NewDecoder(bytes.NewReader(msg.RawChannel))
-	decPayload := ascii85.NewDecoder(bytes.NewReader(msg.RawPayload))
-	chbyte, err := io.ReadAll(decChannel)
-	if err != nil {
-		t.decodeFailureLogHdl.DecodeChannelAscii85Failed(msg.RawChannel, err)
-		return
-	}
-	chnum := binary.LittleEndian.Uint32(chbyte)
-	payload, err := io.ReadAll(decPayload)
-	if err != nil {
-		t.decodeFailureLogHdl.DecodePayloadAscii85Failed(chnum, msg.RawPayload, err)
-		return
-	}
-	if t.enableCRC {
-		if len(msg.Checksum) <= 0 {
-			t.decodeFailureLogHdl.NeedCRCChecksum()
-			return
-		}
-		decCRC := ascii85.NewDecoder(bytes.NewReader(msg.Checksum))
-		crcBytes, err := io.ReadAll(decCRC)
-		if err != nil {
-			t.decodeFailureLogHdl.DecodeChecksumAscii85Failed(chnum, msg.RawPayload, err)
-			return
-		}
-		crcNum := binary.LittleEndian.Uint32(crcBytes)
-		crcABytes := make([]byte, 0, len(payload)+4)
-		crcABytes = append(crcABytes, chbyte...)
-		crcABytes = append(crcABytes, payload...)
-		crcAChecksum := crc32.ChecksumIEEE(crcABytes)
-		if crcNum != crcAChecksum {
-			t.decodeFailureLogHdl.CRCChecksumMismached(crcAChecksum, crcNum)
-			return
-		}
-	}
-	omsg := &Message{
-		Channel: chnum,
-		Payload: payload,
-	}
-	t.outch <- omsg
-}