From 6c9102f75fe4440f52dc908d61bce579a333b682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BA=90=E6=96=87=E9=9B=A8?= <41315874+fumiama@users.noreply.github.com> Date: Mon, 26 Feb 2024 03:44:30 +0900 Subject: [PATCH] feat: add iterator --- reader.go | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ reader_test.go | 42 +++++++++++++++++++++++++++++++ spb.go | 11 +++++---- 3 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 reader.go create mode 100644 reader_test.go diff --git a/reader.go b/reader.go new file mode 100644 index 0000000..c8aa5db --- /dev/null +++ b/reader.go @@ -0,0 +1,67 @@ +package spb + +import "io" + +type Iterator struct { + r io.Reader + structlen, reallen uint32 + i uint32 + v []byte +} + +// NewReader 迭代器形式读取而非一次解析完 +func NewReader(r io.Reader) (it Iterator, err error) { + it.structlen, it.reallen, err = ReadNum(r) + if err != nil { + return + } + if it.structlen <= 1 || it.structlen >= uint32(1)<<20 { + err = ErrInvalidStructLen + return + } + it.r = r + return +} + +// Next 是否有下一个 +func (it *Iterator) Next() bool { + if it.i >= it.structlen { + return false + } + var offset, datalen, n uint32 + var err error + offset, n, err = ReadNum(it.r) + it.reallen += n + if err != nil { + return false + } + datalen, n, err = ReadNum(it.r) + it.reallen += n + if err != nil { + return false + } + if datalen == 0 { + return false + } + if datalen > offset { + return false + } + it.v = append(it.v[:0], make([]byte, datalen)...) + x, err := it.r.Read(it.v) + it.reallen += uint32(x) + if err != nil { + return false + } + it.i += offset + return true +} + +// Bytes 本次迭代的原始值 +func (it *Iterator) Bytes() []byte { + return it.v +} + +// String 将本次迭代的值解释为 string +func (it *Iterator) String() string { + return string(it.v) +} diff --git a/reader_test.go b/reader_test.go new file mode 100644 index 0000000..588df3c --- /dev/null +++ b/reader_test.go @@ -0,0 +1,42 @@ +package spb + +import ( + "bufio" + "fmt" + "os" + "testing" +) + +func TestReader(t *testing.T) { + f, err := os.Open("dict.sp") + if err != nil { + t.Fatal(err) + } + defer f.Close() + ft, err := os.Open("dict.txt") + if err != nil { + t.Fatal(err) + } + defer ft.Close() + sc := bufio.NewScanner(ft) + var s Iterator + i := 1 + for sc.Scan() { + s, err = NewReader(f) + if err != nil { + break + } + if !s.Next() { + t.Fatal("unexpected no next") + } + t0 := s.String() + if !s.Next() { + t.Fatal("unexpected no next") + } + t1 := s.String() + if fmt.Sprint(t0, "\t", t1) != sc.Text() { + t.Fatal("invalid text @ line", i) + } + i++ + } +} diff --git a/spb.go b/spb.go index 73e63ba..943feb3 100644 --- a/spb.go +++ b/spb.go @@ -2,7 +2,7 @@ package spb import "io" -func ReadNum(r io.Reader) (n uint32, cnt int, err error) { +func ReadNum(r io.Reader) (n uint32, cnt uint32, err error) { var buf [1]byte for cnt < 5 { _, err = r.Read(buf[:]) @@ -43,7 +43,7 @@ type SimplePB struct { } func NewSimplePB(r io.Reader) (s SimplePB, err error) { - cnt := 0 + cnt := uint32(0) s.StructLen, cnt, err = ReadNum(r) if err != nil { s.RealLen = uint32(cnt) @@ -55,7 +55,7 @@ func NewSimplePB(r io.Reader) (s SimplePB, err error) { return } var offset, datalen uint32 - n := 0 + n := uint32(0) for i := uint32(0); i < s.StructLen; i += offset { offset, n, err = ReadNum(r) cnt += n @@ -75,8 +75,9 @@ func NewSimplePB(r io.Reader) (s SimplePB, err error) { break } t := make([]byte, datalen) - n, err = r.Read(t) - cnt += n + x := 0 + x, err = r.Read(t) + cnt += uint32(x) if err != nil { break }