+
+type MTHSeqEnt struct {
+ l int
+ h [MTHSize]byte
+}
+
+type MTHSeq struct {
+ hasherLeaf *blake3.Hasher
+ hasherNode *blake3.Hasher
+ hashes []MTHSeqEnt
+ buf *bytes.Buffer
+ events chan MTHEvent
+ ctrs []int
+ finished bool
+}
+
+func MTHSeqNew() *MTHSeq {
+ mth := MTHSeq{
+ hasherLeaf: blake3.New(MTHSize, MTHLeafKey[:]),
+ hasherNode: blake3.New(MTHSize, MTHNodeKey[:]),
+ buf: bytes.NewBuffer(make([]byte, 0, 2*MTHBlockSize)),
+ ctrs: make([]int, 1, 2),
+ }
+ return &mth
+}
+
+func (mth *MTHSeq) Reset() { panic("not implemented") }
+
+func (mth *MTHSeq) Size() int { return MTHSize }
+
+func (mth *MTHSeq) BlockSize() int { return MTHBlockSize }
+
+func (mth *MTHSeq) PrependFrom(r io.Reader) (int, error) {
+ panic("must not reach that code")
+}
+
+func (mth *MTHSeq) Events() chan MTHEvent {
+ mth.events = make(chan MTHEvent)
+ return mth.events
+}
+
+func (mth *MTHSeq) SetPktName(pktName string) {}
+
+func (mth *MTHSeq) PrependSize() int64 { return 0 }
+
+func (mth *MTHSeq) leafAdd() {
+ ent := MTHSeqEnt{l: 0}
+ mth.hasherLeaf.Sum(ent.h[:0])
+ mth.hasherLeaf.Reset()
+ mth.hashes = append(mth.hashes, ent)
+ if mth.events != nil {
+ mth.events <- MTHEvent{
+ MTHEventAppend, 0, mth.ctrs[0],
+ mth.hashes[len(mth.hashes)-1].h[:],
+ }
+ }
+ mth.ctrs[0]++
+}
+
+func (mth *MTHSeq) incr(l int) {
+ if len(mth.ctrs) <= l {
+ mth.ctrs = append(mth.ctrs, 0)
+ } else {
+ mth.ctrs[l]++
+ }
+}
+
+func (mth *MTHSeq) fold() {
+ for len(mth.hashes) >= 2 {
+ if mth.hashes[len(mth.hashes)-2].l != mth.hashes[len(mth.hashes)-1].l {
+ break
+ }
+ if _, err := mth.hasherNode.Write(mth.hashes[len(mth.hashes)-2].h[:]); err != nil {
+ panic(err)
+ }
+ if _, err := mth.hasherNode.Write(mth.hashes[len(mth.hashes)-1].h[:]); err != nil {
+ panic(err)
+ }
+ mth.hashes = mth.hashes[:len(mth.hashes)-1]
+ end := &mth.hashes[len(mth.hashes)-1]
+ end.l++
+ mth.incr(end.l)
+ mth.hasherNode.Sum(end.h[:0])
+ mth.hasherNode.Reset()
+ if mth.events != nil {
+ mth.events <- MTHEvent{MTHEventFold, end.l, mth.ctrs[end.l], end.h[:]}
+ }
+ }
+}
+
+func (mth *MTHSeq) Write(data []byte) (int, error) {
+ if mth.finished {
+ return 0, errors.New("already Sum()ed")
+ }
+ n, err := mth.buf.Write(data)
+ if err != nil {
+ return n, err
+ }
+ for mth.buf.Len() >= MTHBlockSize {
+ if _, err = mth.hasherLeaf.Write(mth.buf.Next(MTHBlockSize)); err != nil {
+ return n, err
+ }
+ mth.leafAdd()
+ mth.fold()
+ }
+ return n, err
+}
+
+func (mth *MTHSeq) Sum(b []byte) []byte {
+ if mth.finished {
+ return append(b, mth.hashes[0].h[:]...)
+ }
+ if mth.buf.Len() > 0 {
+ if _, err := mth.hasherLeaf.Write(mth.buf.Next(MTHBlockSize)); err != nil {
+ panic(err)
+ }
+ mth.leafAdd()
+ mth.fold()
+ }
+ switch mth.ctrs[0] {
+ case 0:
+ if _, err := mth.hasherLeaf.Write(nil); err != nil {
+ panic(err)
+ }
+ mth.leafAdd()
+ fallthrough
+ case 1:
+ mth.hashes = append(mth.hashes, mth.hashes[0])
+ mth.ctrs[0]++
+ if mth.events != nil {
+ mth.events <- MTHEvent{
+ MTHEventAppend, 0, mth.ctrs[0],
+ mth.hashes[len(mth.hashes)-1].h[:],
+ }
+ }
+ mth.fold()
+ }
+ for len(mth.hashes) >= 2 {
+ l := mth.hashes[len(mth.hashes)-2].l
+ mth.incr(l)
+ mth.hashes[len(mth.hashes)-1].l = l
+ if mth.events != nil {
+ mth.events <- MTHEvent{
+ MTHEventAppend, l, mth.ctrs[l],
+ mth.hashes[len(mth.hashes)-1].h[:],
+ }
+ }
+ mth.fold()
+ }
+ mth.finished = true
+ if mth.events != nil {
+ close(mth.events)
+ }
+ return append(b, mth.hashes[0].h[:]...)
+}
+
+func MTHNew(size, offset int64) MTH {
+ if offset == 0 {
+ return MTHSeqNew()
+ }
+ return MTHFatNew(size, offset)
+}