more effort into understanding why TAP interface is so slow on my linux machine to no avail

This commit is contained in:
soypat
2025-06-05 00:47:07 -03:00
parent bc14ed395d
commit 6ba727a0ef
12 changed files with 256 additions and 26 deletions
+2 -2
View File
@@ -13,7 +13,7 @@ const (
IPProtoGGP IPProto = 3 // Gateway-to-Gateway [RFC823]
IPProtoIPv4 IPProto = 4 // IPv4 encapsulation [RFC2003]
IPProtoST IPProto = 5 // Stream [RFC1190, RFC1819]
IPProtoTCP IPProto = 6 // Transmission Control [RFC9293]
IPProtoTCP IPProto = 6 // TCP [RFC9293]
IPProtoCBT IPProto = 7 // CBT [Ballardie]
IPProtoEGP IPProto = 8 // Exterior Gateway Protocol [RFC888]
IPProtoIGP IPProto = 9 // any private interior gateway (used by Cisco for their IGRP)
@@ -24,7 +24,7 @@ const (
IPProtoEMCON IPProto = 14 // EMCON
IPProtoXNET IPProto = 15 // Cross Net Debugger
IPProtoCHAOS IPProto = 16 // Chaos
IPProtoUDP IPProto = 17 // User Datagram [RFC768]
IPProtoUDP IPProto = 17 // UDP [RFC768]
IPProtoMUX IPProto = 18 // Multiplexing
IPProtoDCNMEAS IPProto = 19 // DCN Measurement Subsystems
IPProtoHMP IPProto = 20 // Host Monitoring [RFC869]
+39 -5
View File
@@ -19,6 +19,7 @@ import (
"github.com/soypat/lneto/internal"
"github.com/soypat/lneto/internal/ltesto"
"github.com/soypat/lneto/internet"
"github.com/soypat/lneto/internet/pcap"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/tcp"
)
@@ -65,14 +66,22 @@ func main() {
hdr.Reset(make([]byte, 0, 1024))
const standbyDuration = 5 * time.Second
lastHit := time.Now().Add(-standbyDuration)
var cap pcap.PacketBreakdown
for {
nread, err := tap.Read(buf[:])
if err != nil {
slogger.error("tap-err", slog.String("err", err.Error()))
log.Fatal(err)
} else if nread > 0 {
// debugEthPacket(nil, "IN ", buf[:nread])
// fmt.Println("INHEX ", debugHex(buf[:nread]))
frames, err := cap.CaptureEthernet(nil, buf[:nread], 0)
if err == nil {
flags := getTCPFlags(frames, buf[:nread])
if flags == 0 {
fmt.Println("IN", time.Now().Format("15:04:05.000"), frames)
} else {
fmt.Println("IN", time.Now().Format("15:04:05.000"), frames, flags.String())
}
}
err = lStack.RecvEth(buf[:nread])
if err != nil {
slogger.error("recv", slog.String("err", err.Error()), slog.Int("plen", nread))
@@ -80,11 +89,18 @@ func main() {
}
doHTTP(handler, &hdr)
nw, err := lStack.HandleEth(buf[:])
debugEthPacket(nil, "OUT", buf[:nw])
if err != nil {
slogger.error("handle", slog.String("err", err.Error()))
} else if nw > 0 {
// fmt.Println("OUTHEX ", debugHex(buf[:nread]))
frames, err := cap.CaptureEthernet(nil, buf[:nread], 0)
if err == nil {
flags := getTCPFlags(frames, buf[:nread])
if flags == 0 {
fmt.Println("OU", time.Now().Format("15:04:05.000"), frames)
} else {
fmt.Println("OU", time.Now().Format("15:04:05.000"), frames, flags.String())
}
}
_, err = tap.Write(buf[:nw])
if err != nil {
log.Fatal(err)
@@ -92,7 +108,7 @@ func main() {
}
hit := nread > 0 || nw > 0
if hit {
slogger.info("exchange", slog.Int("read", nread), slog.Int("nwrite", nw))
// slogger.info("exchange", slog.Int("read", nread), slog.Int("nwrite", nw))
lastHit = time.Now()
} else {
if time.Since(lastHit) > standbyDuration {
@@ -379,3 +395,21 @@ func debugHex(b []byte) string {
}
const tblhex = "0123456789abcdef"
func getTCPFlags(frames []pcap.Frame, pkt []byte) (flags tcp.Flags) {
for i := range frames {
if frames[i].Protocol != lneto.IPProtoTCP {
continue
}
iflags, err := frames[i].FieldByClass(pcap.FieldClassFlags)
if err != nil {
return 0
}
v, err := frames[i].FieldAsUint(iflags, pkt)
if err != nil {
return 0
}
return tcp.Flags(v)
}
return 0
}
+34 -1
View File
@@ -11,8 +11,10 @@ import (
"runtime"
"time"
"github.com/soypat/lneto"
"github.com/soypat/lneto/internal/ltesto"
"github.com/soypat/lneto/internet/pcap"
"github.com/soypat/lneto/tcp"
)
func main() {
@@ -45,7 +47,14 @@ func run() error {
captime := time.Now()
frames, err := cap.CaptureEthernet(nil, pkt, 0)
if err == nil {
fmt.Println(channel, captime.Format("15:04:05.000"), frames)
flags, src, dst := getTCPData(frames, pkt)
if flags != 0 {
fmt.Println(channel, captime.Format("15:04:05.000"), frames, flags.String(), src, "->", dst)
} else {
fmt.Println(channel, captime.Format("15:04:05.000"), frames)
}
} else {
fmt.Println(channel, captime.Format("15:04:05.000"), "ERR", frames, err.Error())
}
})
hwaddr, err := sv.HardwareAddress6()
@@ -74,3 +83,27 @@ func run() error {
}
}
}
func getTCPData(frames []pcap.Frame, pkt []byte) (flags tcp.Flags, src, dst uint16) {
for i := range frames {
if frames[i].Protocol != lneto.IPProtoTCP {
continue
}
return tcp.Flags(getFrameClassUint(frames[i], pkt, pcap.FieldClassFlags)),
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassSrc)),
uint16(getFrameClassUint(frames[i], pkt, pcap.FieldClassDst))
}
return 0, 0, 0
}
func getFrameClassUint(frame pcap.Frame, pkt []byte, class pcap.FieldClass) uint64 {
iflags, err := frame.FieldByClass(class)
if err != nil {
return 0
}
v, err := frame.FieldAsUint(iflags, pkt)
if err != nil {
return 0
}
return v
}
+4 -4
View File
@@ -7,6 +7,7 @@ import (
"os"
"os/signal"
"syscall"
"time"
"github.com/soypat/lneto/http/httpraw"
)
@@ -33,7 +34,7 @@ func run() error {
return err
}
fmt.Println("dialing...")
fmt.Println(time.Now().Format("15:04:05.000"), "dialing...")
conn, err := net.DialTCP("tcp4", &net.TCPAddr{IP: []byte{192, 168, 10, 1}, Port: port}, &net.TCPAddr{IP: []byte{192, 168, 10, 2}, Port: 80})
if err != nil {
return err
@@ -47,9 +48,8 @@ func run() error {
conn.Close()
os.Exit(0)
}()
fmt.Println("reading...")
fmt.Println(time.Now().Format("15:04:05.000"), "writing...")
conn.Write(req)
hdr.Reset(nil)
var needMore bool = true
for needMore {
@@ -63,6 +63,6 @@ func run() error {
if err != nil {
return err
}
fmt.Println("got HTTP:\n", hdr.String())
fmt.Println(time.Now().Format("15:04:05.000"), "got HTTP:\n", hdr.String())
return nil
}
+8 -1
View File
@@ -4,7 +4,9 @@ import (
"errors"
"io"
"log/slog"
"net"
"net/netip"
"slices"
"github.com/soypat/lneto"
"github.com/soypat/lneto/internal"
@@ -66,7 +68,12 @@ func (sb *StackBasic) Recv(frame []byte) error {
proto := ifrm.Protocol()
if h.proto == proto {
sb.info("iprecv", slog.String("ipproto", proto.String()), slog.Int("plen", int(totalLen)))
return h.recv(frame[:totalLen], off)
err = h.recv(frame[:totalLen], off)
if err == net.ErrClosed {
sb.info("ipclose", slog.String("proto", proto.String()))
sb.handlers = slices.Delete(sb.handlers, i, i+1)
}
return err
}
}
}
+135 -5
View File
@@ -12,9 +12,13 @@ import (
"github.com/soypat/lneto/ethernet"
"github.com/soypat/lneto/http/httpraw"
"github.com/soypat/lneto/ipv4"
"github.com/soypat/lneto/ipv6"
"github.com/soypat/lneto/tcp"
"github.com/soypat/lneto/udp"
)
const unknownPayloadProto = "payload?"
type PacketBreakdown struct {
hdr httpraw.Header
vld lneto.Validator
@@ -115,6 +119,29 @@ func (pc *PacketBreakdown) CaptureARP(dst []Frame, pkt []byte, bitOffset int) ([
return dst, nil
}
func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
if bitOffset%8 != 0 {
return dst, errors.New("IPv6 must be parsed at byte boundary")
}
ifrm6, err := ipv6.NewFrame(pkt[bitOffset/8:])
if err != nil {
return dst, err
}
ifrm6.ValidateSize(pc.validator())
if pc.validator().HasError() {
return dst, pc.validator().Err()
}
finfo := Frame{
Protocol: ethernet.TypeIPv6,
PacketBitOffset: bitOffset,
}
finfo.Fields = append(finfo.Fields, baseIPv6Fields[:]...)
dst = append(dst, finfo)
proto := ifrm6.NextHeader()
end := 40 * octet
return pc.captureIPProto(proto, dst, pkt, end)
}
func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
if bitOffset%8 != 0 {
return dst, errors.New("IPv4 must be parsed at byte boundary")
@@ -143,11 +170,17 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) (
proto := ifrm4.Protocol()
dst = append(dst, finfo)
end := bitOffset + octet*ifrm4.HeaderLength()
return pc.captureIPProto(proto, dst, pkt, end)
}
func (pc *PacketBreakdown) captureIPProto(proto lneto.IPProto, dst []Frame, pkt []byte, bitOffset int) (_ []Frame, err error) {
switch proto {
case lneto.IPProtoTCP:
dst, err = pc.CaptureTCP(dst, pkt, end)
dst, err = pc.CaptureTCP(dst, pkt, bitOffset)
case lneto.IPProtoUDP:
dst, err = pc.CaptureUDP(dst, pkt, bitOffset)
default:
dst = append(dst, remainingFrameInfo(proto, 0, end, octet*len(pkt)))
dst = append(dst, remainingFrameInfo(proto, 0, bitOffset, octet*len(pkt)))
}
return dst, err
}
@@ -183,12 +216,35 @@ func (pc *PacketBreakdown) CaptureTCP(dst []Frame, pkt []byte, bitOffset int) ([
if len(payload) > 0 {
dst, err = pc.CaptureHTTP(dst, pkt, end)
if err != nil {
dst = append(dst, remainingFrameInfo(nil, FieldClassPayload, end, len(pkt)))
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
}
}
return dst, nil
}
func (pc *PacketBreakdown) CaptureUDP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
if bitOffset%8 != 0 {
return dst, errors.New("UDP must be parsed at byte boundary")
}
ufrm, err := udp.NewFrame(pkt[bitOffset/8:])
if err != nil {
return dst, err
}
ufrm.ValidateSize(pc.validator())
if pc.validator().HasError() {
return dst, pc.validator().Err()
}
finfo := Frame{
Protocol: lneto.IPProtoUDP,
PacketBitOffset: bitOffset,
}
finfo.Fields = append(finfo.Fields, baseUDPFields[:]...)
dst = append(dst, finfo)
end := bitOffset + 8*octet
dst = append(dst, remainingFrameInfo(unknownPayloadProto, FieldClassPayload, end, octet*len(pkt)))
return dst, nil
}
func (pc *PacketBreakdown) CaptureHTTP(dst []Frame, pkt []byte, bitOffset int) ([]Frame, error) {
const httpProtocol = "HTTP"
if bitOffset%8 != 0 {
@@ -357,7 +413,6 @@ func (frm Frame) AppendField(dst []byte, fieldIdx int, pkt []byte) ([]byte, erro
func (frm Frame) String() string {
iopt, err := frm.FieldByClass(FieldClassOptions)
hasOpts := ""
if err == nil {
hasOpts = fmt.Sprintf(" optlen=%d", (frm.Fields[iopt].BitLength+7)/8)
@@ -457,6 +512,56 @@ var baseARPFields = [...]FrameField{
},
}
var baseIPv6Fields = [...]FrameField{
{
Class: FieldClassVersion,
FrameBitOffset: 0,
BitLength: 4,
},
{
Name: "Type of Service",
Class: FieldClassFlags,
FrameBitOffset: 4,
BitLength: 1 * octet,
RightAligned: true,
},
{
Name: "Flow Label",
Class: FieldClassID,
FrameBitOffset: 12,
BitLength: 20,
RightAligned: true,
},
{
Name: "Total Length",
Class: FieldClassSize,
FrameBitOffset: 4 * octet,
BitLength: 2 * octet,
},
{
Name: "Next Header",
Class: 0,
FrameBitOffset: 6 * octet,
BitLength: 1 * octet,
},
{
Name: "Hop Limit",
Class: 0,
FrameBitOffset: 7 * octet,
BitLength: 1 * octet,
},
{
Class: FieldClassSrc,
FrameBitOffset: 8 * octet,
BitLength: 16 * octet,
},
{
Class: FieldClassSrc,
FrameBitOffset: 24 * octet,
BitLength: 16 * octet,
},
}
var baseIPv4Fields = [...]FrameField{
{
Class: FieldClassVersion,
@@ -532,7 +637,7 @@ var baseTCPFields = [...]FrameField{
},
{
Name: "Destination port",
Class: FieldClassSrc,
Class: FieldClassDst,
FrameBitOffset: 2 * octet,
BitLength: 2 * octet,
},
@@ -579,6 +684,31 @@ var baseTCPFields = [...]FrameField{
},
}
var baseUDPFields = [...]FrameField{
{
Name: "Source port",
Class: FieldClassSrc,
FrameBitOffset: 0,
BitLength: 2 * octet,
},
{
Name: "Destination port",
Class: FieldClassDst,
FrameBitOffset: 2 * octet,
BitLength: 2 * octet,
},
{
Class: FieldClassSize,
FrameBitOffset: 4 * octet,
BitLength: 2 * octet,
},
{
Class: FieldClassChecksum,
FrameBitOffset: 6 * octet,
BitLength: 2 * octet,
},
}
func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int) Frame {
return Frame{
Protocol: proto,
+10
View File
@@ -102,6 +102,16 @@ func TestCap(t *testing.T) {
if gotTCPFlags != wantTCPflags {
t.Errorf("want %s TCP flags, got %s", wantTCPflags.String(), gotTCPFlags.String())
}
wanDstPort := gen.DstTCP
wantSrcPort := gen.SrcTCP
gotSrcPort := uint16(getClass(ptfrm, FieldClassSrc))
gotDstPort := uint16(getClass(ptfrm, FieldClassDst))
if wantSrcPort != gotSrcPort {
t.Errorf("want %d TCP src port, got %d", wantSrcPort, gotSrcPort)
}
if wanDstPort != gotDstPort {
t.Errorf("want %d TCP dst port, got %d", wanDstPort, gotDstPort)
}
gotHeaderLen := getClass(ptfrm, FieldClassSize)
if gotHeaderLen != uint64(wantHeaderLen) {
t.Errorf("want %d TCP header length, got %d", wantHeaderLen, gotHeaderLen)
+14
View File
@@ -0,0 +1,14 @@
package internet
import "github.com/soypat/lneto"
type PortStack struct {
handlers []porthandler
proto lneto.IPProto
}
type porthandler struct {
recv func([]byte, int) error
handle func([]byte, int) (int, error)
port uint16
}
+2 -2
View File
@@ -152,7 +152,7 @@ func _() {
}
const (
_IPProto_name_0 = "IPv6 Hop-by-Hop Option [RFC8200]Internet Control Message [RFC792]Internet Group Management [RFC1112]Gateway-to-Gateway [RFC823]IPv4 encapsulation [RFC2003]Stream [RFC1190, RFC1819]Transmission Control [RFC9293]CBT [Ballardie]Exterior Gateway Protocol [RFC888]any private interior gateway (used by Cisco for their IGRP)BBN RCC MonitoringNetwork Voice Protocol [RFC741]PUPARGUSEMCONCross Net DebuggerChaosUser Datagram [RFC768]MultiplexingDCN Measurement SubsystemsHost Monitoring [RFC869]Packet Radio MeasurementXEROX NS IDPTrunk-1Trunk-2Leaf-1Leaf-2Reliable Data Protocol [RFC908]Internet Reliable Transaction [RFC938]ISO Transport Protocol Class 4 [RFC905]Bulk Data Transfer Protocol [RFC998]MFE Network Services ProtocolMERIT Internodal ProtocolDatagram Congestion Control Protocol [RFC4340]Third Party Connect ProtocolInter-Domain Policy Routing ProtocolXTPDatagram Delivery ProtocolIDPR Control Message Transport ProtoTP++ Transport ProtocolIL Transport ProtocolIPv6 encapsulation [RFC2473]Source Demand Routing ProtocolRouting Header for IPv6 [RFC8200]Fragment Header for IPv6 [RFC8200]Inter-Domain Routing ProtocolReservation Protocol [RFC2205]Generic Routing Encapsulation [RFC2784]Dynamic Source Routing ProtocolBNAEncap Security Payload [RFC4303]Authentication Header [RFC4302]Integrated Net Layer Security TUBAIP with EncryptionNBMA Address Resolution ProtocolIP MobilityTransport Layer Security Protocol using Kryptonet key managementSKIPICMP for IPv6 [RFC8200]No Next Header for IPv6 [RFC8200]Destination Options for IPv6 [RFC8200]"
_IPProto_name_0 = "IPv6 Hop-by-Hop Option [RFC8200]Internet Control Message [RFC792]Internet Group Management [RFC1112]Gateway-to-Gateway [RFC823]IPv4 encapsulation [RFC2003]Stream [RFC1190, RFC1819]TCP [RFC9293]CBT [Ballardie]Exterior Gateway Protocol [RFC888]any private interior gateway (used by Cisco for their IGRP)BBN RCC MonitoringNetwork Voice Protocol [RFC741]PUPARGUSEMCONCross Net DebuggerChaosUDP [RFC768]MultiplexingDCN Measurement SubsystemsHost Monitoring [RFC869]Packet Radio MeasurementXEROX NS IDPTrunk-1Trunk-2Leaf-1Leaf-2Reliable Data Protocol [RFC908]Internet Reliable Transaction [RFC938]ISO Transport Protocol Class 4 [RFC905]Bulk Data Transfer Protocol [RFC998]MFE Network Services ProtocolMERIT Internodal ProtocolDatagram Congestion Control Protocol [RFC4340]Third Party Connect ProtocolInter-Domain Policy Routing ProtocolXTPDatagram Delivery ProtocolIDPR Control Message Transport ProtoTP++ Transport ProtocolIL Transport ProtocolIPv6 encapsulation [RFC2473]Source Demand Routing ProtocolRouting Header for IPv6 [RFC8200]Fragment Header for IPv6 [RFC8200]Inter-Domain Routing ProtocolReservation Protocol [RFC2205]Generic Routing Encapsulation [RFC2784]Dynamic Source Routing ProtocolBNAEncap Security Payload [RFC4303]Authentication Header [RFC4302]Integrated Net Layer Security TUBAIP with EncryptionNBMA Address Resolution ProtocolIP MobilityTransport Layer Security Protocol using Kryptonet key managementSKIPICMP for IPv6 [RFC8200]No Next Header for IPv6 [RFC8200]Destination Options for IPv6 [RFC8200]"
_IPProto_name_1 = "CFTP"
_IPProto_name_2 = "SATNET and Backroom EXPAKKryptolanMIT Remote Virtual Disk ProtocolInternet Pluribus Packet Core"
_IPProto_name_3 = "SATNET MonitoringVISA ProtocolInternet Packet Core UtilityComputer Protocol Network ExecutiveComputer Protocol Heart BeatWang Span NetworkPacket Video ProtocolBackroom SATNET MonitoringSUN ND PROTOCOL-TemporaryWIDEBAND MonitoringWIDEBAND EXPAKISO Internet ProtocolVMTPSECURE-VMTPVINESTTPNSFNET-IGPDissimilar Gateway ProtocolTCFEIGRPOSPFIGPSprite RPC ProtocolLocus Address Resolution ProtocolMulticast Transport ProtocolAX.25 FramesIP-within-IP Encapsulation ProtocolMobile Internetworking Control Pro.Semaphore Communications Sec. Pro.Ethernet-within-IP EncapsulationEncapsulation Header"
@@ -161,7 +161,7 @@ const (
)
var (
_IPProto_index_0 = [...]uint16{0, 32, 65, 100, 127, 155, 180, 210, 225, 259, 318, 336, 367, 370, 375, 380, 398, 403, 425, 437, 463, 487, 511, 523, 530, 537, 543, 549, 580, 618, 657, 693, 722, 747, 793, 821, 857, 860, 886, 922, 945, 966, 994, 1024, 1057, 1091, 1120, 1150, 1189, 1220, 1223, 1255, 1286, 1320, 1338, 1370, 1381, 1445, 1449, 1472, 1505, 1543}
_IPProto_index_0 = [...]uint16{0, 32, 65, 100, 127, 155, 180, 193, 208, 242, 301, 319, 350, 353, 358, 363, 381, 386, 398, 410, 436, 460, 484, 496, 503, 510, 516, 522, 553, 591, 630, 666, 695, 720, 766, 794, 830, 833, 859, 895, 918, 939, 967, 997, 1030, 1064, 1093, 1123, 1162, 1193, 1196, 1228, 1259, 1293, 1311, 1343, 1354, 1418, 1422, 1445, 1478, 1516}
_IPProto_index_2 = [...]uint8{0, 25, 34, 66, 95}
_IPProto_index_3 = [...]uint16{0, 17, 30, 58, 93, 121, 138, 159, 185, 210, 229, 243, 264, 268, 279, 284, 287, 297, 324, 327, 332, 339, 358, 391, 419, 431, 466, 501, 535, 567, 587}
_IPProto_index_4 = [...]uint8{0, 4, 36, 48, 78, 82, 86, 89, 104, 135, 159, 179, 188, 222, 253}
+1 -1
View File
@@ -57,7 +57,7 @@ type ControlBlock struct {
challengeAck bool
}
// State returns the current state of the TCP connection.
// State returns the current state of the TCP connection. See [State].
func (tcb *ControlBlock) State() State { return tcb._state }
// RecvNext returns the next sequence number expected to be received from remote.
+6 -4
View File
@@ -308,15 +308,17 @@ func (s State) IsSynchronized() bool {
return s >= StateEstablished && !s.IsClosed()
}
// txOpen returns true if the TCP state machine allows data to be sent by user.
func (s State) txOpen() bool {
// TxDataOpen returns true if the state allows for outgoing data segments to be sent.
// Combine with [State.IsPreestablished] to know whether there is no more data to be sent over the network.
func (s State) TxDataOpen() bool {
// In CloseWait state the remote endpoint has closed
// our receive hald of the connection but we can still transmit indefinitely.
return s == StateEstablished || s == StateCloseWait
}
// rxOpen returns true if the TCP state machine allows data to be received from remote endpoint.
func (s State) rxOpen() bool {
// RxDataOpen returns true if the state allows the receiving of incoming data segments.
// Combine with [State.IsPreestablished] to know whether there is no more data to be received over the network.
func (s State) RxDataOpen() bool {
return s == StateEstablished || s == StateFinWait1 || s == StateFinWait2
}
+1 -1
View File
@@ -65,7 +65,7 @@ func (v *Validator) AddBitPosErr(bitStart, bitLen int, err error) {
if err == nil {
panic("err argument to bitPosErr cannot be nil")
} else if bitLen <= 0 {
panic("")
panic("zero bitlen")
}
v.accumBitpos = append(v.accumBitpos, BitPosErr{BitStart: bitStart, BitLen: bitLen, Err: err})
v.accum = append(v.accum, &v.accumBitpos[len(v.accumBitpos)-1])