Documentation
¶
Overview ¶
Bitpacked data pretty-formatter. Makes bits human readable. Zero dependencies. Every single input bit from 0 to 63 can print a label that show this bit state. Arbitrary group of bits can be printed as decimal, octal or hex numbers and as C32s, Ascii7b or UTF8 characters. Plus as an IPv4 address in dot-notation. Taste it:
var header uint64 = 0xafdfdeadbeef4d0e
fmt.Printf("%s\n", bitpeek.Snap(
`'Type:'F 'EXT=.ACK= Id:0xFHH from IPv4.Address32@:D.16@`, header))
Output:
Type:5 ext.ACK Id:0x7DF from 222.173.190.239:19726
// Benchmark: 277 ns/op 64 B/op 1 allocs/op (Sprintf: 862 ns/op)
// EscAnalysis: make([]byte, oi) escapes to heap
Package has NO dependencies and its parser is under 170 LoC so it is useful where standard "fmt" and "log" packages are too heavy to use (ie. IoT, embed and high-throughput environments). Parser allocates heap memory only for its output. Pic (format) string is written in left-to-right order (most significant bit, b63 is on the left) so any shorter uint based type can be simply cast and fed to Snap function. Bitpeek has an accompanying tool (bplint) you ought to use to validate all picstrings in your source file(s).
BITPEEK FORMAT STRING
QUOTES & LABELS
\ Escape : Next character is not interpreted except \n for
NL, \t for TAB and \' for ' in quoted text.
'quoted text' : not interpreted. Exceptions given above.
'label* : * means one of =<?> chars. Labels can chain so
only one opening ' is needed for the whole set.
Eg. 'TX= ACK< ERR:? are three labels in a chain.
Label can contain ESCAPED=<'>? chars. Otherwise
its just quoted text due to opening '.
unquoted text : Unescaped ABCEFGH@<'>?= characters are commands.
Escapes \n\t work, other chars are emitted asis.
COMMANDS
takes : emits (emits == outputs)
? - 1 bit : label with digit 0/1 in place of '?'
> - 1 bit : label - only if bit is SET (1). Otherwise skips.
< - 1 bit : label - only if bit is UNSET (0). Otherwise skips.
= - 1 bit : label lowercased if bit is UNSET. From UC in pic.
B - 1 bit : digit 0 1 : B for bit
E - 2 bits : digit 0..3 :
F - 3 bits : digit 0..7 :
H - 4 bits : hex digit 0..F : H for Hex
G - 5 bits : C32s character : (CRC spelling code)
A - 7 bits : 7b char/ascii; : A for Ascii (emits ~ for A < 32)
C - 8 bits : 8b char/utf8; : C for Char (emits ~ for C < 32)
I -32 bits : IPv4 address : Pic is IPv4.Address32@
D -dd bits : Decimal number : Pic is D.dd@ 01< dd <16.
! -dd bits : SKIP 'dd' bits : Pic is !dd@ (>>dd) 01< dd <63.
@ : dd@ (bitcount) : two digit number of bits to take.
Picstrings linter is avaliable at https://github.com/ohir/bplint
go get github.com/ohir/bplint
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Snap ¶
Func Snap takes a string and an uint64 as input data. It returns byteslice filled with printable characters as directed by pic (format) string. Pic string represents b63 on its left and b0 on the right. Parser starts at b0 so shorter ints can be simply cast.
Notes ¶
➊ Use !dd@ skips if you are interested only in bits on higher positions:
fmt.Printf("%s\n", bitpeek.Snap( // 48 to skip on the left of @.
`'Type:'F 'EXT=.ACK= Id:0xFHH!48@`, 0xafdfdeadbeef4d0e))
Output:
Type:5 ext.ACK Id:0x7DF
➋ UTF8 and ascii control characters (eg. NL) are passed as-is so you can make readable conditional "label-line":
bitpeek.Snap(`' This line will show only if bit b1 is set> This line will show only if bit b0 is unset<`, header)
➌ It is possible to omit opening ' for a label at the start of the pic string:
pic := `' Label<` // pic := ` Label<` // same effect as above
➍ H commands are grouped so \HHHH is a pic for 16b number in spite of escape (backslash) in front of first H. Use 'H'HHH if you really need literal H glued to the front of hex digits.
➎ \n\t escapes are always interpreted - even in a quoted text. There is no way to output literal `\n` or `\t`. Don't try.
➏ Annotate all your strings with bplinter tag in form of special comment put ABOVE the line(s) with the pictring itself:
//bitpeek:tag:skip
Optional ":tag" field is used to match with linter's -m option. Picstring tags need not to be unique. Optional ":skip" number tells linter to skip a few (up to 7) next strings. It helps where the picstring in the source is a part of a longer literal:
//bitpeek:sometag:1
{`Example`, `Type:'F 'EXT=.ACK= Id:0xFHH from IPv4.Address32@:D.16@`},
// :1 skips string `Example`
Example (AllCommands) ¶
// Show format commands in action:
//
var hdr uint64 = 0x7841AAbeefFDd37E
fmt.Printf("%s\n", // use bitpeek.Snap, Luke!
Snap(`'Show ALL' ________________________________
'❶ Labels:' 'SYN=.ACK<.ERR>.EXT= with 0 1 1 1 bits
'❷ Labels:' 'SYN=.ACK<.ERR>.EXT= with 1 0 0 0 bits
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
' ‾‾‾‾‾‾‾‾ label chain: SYN=.ACK<.ERR>.EXT='
' Char C:' C
' Label ?:' 'BitIs: ?
' Ascii A:' A
' Decimal:' D.08@ ('D.08@')
' Hex H:' 0xHH ('0xHH')
' Octal :' 0EFF ('0EFF')
' C32s G:' GG ('GG')
' Three F:' F
' Duo E:' E
' Bit B:' B
' Quoted:' '偩 =<\'>?ABCDEFGH\t_Tab\t_Tab\n NewLine: \\backslash ԹՖ'
' Escapes:' 偩 \=\<\'\>\?\A\B\C\D\E\F\G\H\t_Tab\t_Tab\n NewLine: \\backslash ԹՖ
`, hdr))
// print hdr as flags, crc, address :port
// I##.###.###.32@ picture is valid too for IPv4.
fmt.Printf("%s\n\n%s\n",
Snap(`(SYN= ACK= ERR= EXT= OVL= RTX= "GG") 'From: 'IPv4.Address32@:D.16@`, hdr),
Snap(`--- Snap raCCCCCCD.16@ ns ---`, 0x6e20666f72200e0d))
Output: Show ALL ________________________________ ❶ Labels: syn.ERR.EXT with 0 1 1 1 bits ❷ Labels: SYN.ACK.ext with 1 0 0 0 bits ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ ‾‾‾‾‾‾‾‾ label chain: SYN=.ACK<.ERR>.EXT= Char C: A Label ?: BitIs: 1 Ascii A: * Decimal: 190 (D.08@) Hex H: 0xEF (0xHH) Octal : 0375 (0EFF) C32s G: 2n (GG) Three F: 7 Duo E: 3 Bit B: 0 Quoted: 偩 =<'>?ABCDEFGH _Tab _Tab NewLine: \backslash ԹՖ Escapes: 偩 =<'>?ABCDEFGH _Tab _Tab NewLine: \backslash ԹՖ (syn ACK ERR EXT OVL rtx "cb") From: 170.190.239.253:54142 --- Snap ran for 3597 ns ---
Example (BitpeekVsFmtPrintf) ¶
It is possible to use Printf family to show bits albeit we're restricted to 'This:1' 'That:0' indicators. Not to mention poetry of bitshifts and masks that bugs love!
// given packet spec:
// |63|62|61| 60| 59|58|57|56|55|54|53|52|51|50|49|48| BIT
// |GBU Type|ext|ack| Session Id | HEADER
// |47 Source Address H 33| IPv4
// |32 Source Address L 16| IPv4
// |15 Source Port 0| Port
var packet uint64 = 0xafdfdeadbeef4d0e
// Printf
fmt.Printf("Type:%d Ext:%1d Ack:%1d Id:0x%03X from %d.%d.%d.%d:%d :printf\n",
// Are those shifts and masks valid?
packet>>61, packet>>60&1, packet>>59&1, packet>>48&0x7FF,
packet>>40&255, packet>>32&255, packet>>24&255, packet>>16&255, packet&0xffff)
// Bitpeek
fmt.Printf("%s :bitpeek\n",
Snap(`Type:'F 'Ext:? Ack:? Id:0xFHH from IPv4:Address32@:D.16@`, packet))
// fmt.Sprintf 865 ns/op 128 B/op 9 allocs/op // returns string
// bitpeek.Snap 280 ns/op 64 B/op 1 allocs/op // returns []byte
Output: Type:5 Ext:0 Ack:1 Id:0x7DF from 222.173.190.239:19726 :printf Type:5 Ext:0 Ack:1 Id:0x7DF from 222.173.190.239:19726 :bitpeek
Example (Decimals) ¶
Standard Command list shows D.dd@ picture giving decimal for numbers up to 16b. In fact Snap will cope with numbers up to 64b in length given proper pic. Below are generated pics for numbers in 17-64b range. Copy, paste & enjoy:
var fil string = `................`
var spa string = ` `
var d, e, f, pf int
fmt.Printf("D..17@")
for e = 18; e < 65; e++ {
d = e / 3
f = d - 5
if f > pf {
fmt.Println("")
} else {
fmt.Printf("%s", spa[f:])
}
fmt.Printf("D%d%s%d@", e, fil[0:f], e)
pf = f
}
Output: D..17@ D18.18@ D19.19@ D20.20@ D21..21@ D22..22@ D23..23@ D24...24@ D25...25@ D26...26@ D27....27@ D28....28@ D29....29@ D30.....30@ D31.....31@ D32.....32@ D33......33@ D34......34@ D35......35@ D36.......36@ D37.......37@ D38.......38@ D39........39@ D40........40@ D41........41@ D42.........42@ D43.........43@ D44.........44@ D45..........45@ D46..........46@ D47..........47@ D48...........48@ D49...........49@ D50...........50@ D51............51@ D52............52@ D53............53@ D54.............54@ D55.............55@ D56.............56@ D57..............57@ D58..............58@ D59..............59@ D60...............60@ D61...............61@ D62...............62@ D63................63@ D64................64@
Example (FormatterFactory) ¶
If a single 'header type' has many forms (bluetooth anyone?) you may use a formatter factory and 'shape' tables:
// // Make a formatter factory for EHeader
// func as(pic string) (r func(EHeader) []byte) {
// return func(x EHeader) []byte {
// return bitpeek.Snap(pic, uint64(x))
// }
// }
notimp := as(` Unknown packet type! (F!13@)`) // not all types are implemented
var phPPs = []func(EHeader) []byte{
as(`'Intaps: REP=.GRE=.SAB=.UMG=.DAG=.ERR= ml:A`), // pt(3b):rep:gre:sab:umg:dag:err:middleLetter
as(`'CRCspe:' GG !02@'(Error detected!)>`), // pt(3b):CRC(10b): : :err
notimp, // reserved
notimp, // reserved
as(`'LinkUP:' for D.13@ seconds`), // pt(3b):uptime(13b)
as(`' seen: PT:'F 'EXT=.ACK= Id:0xFHH`), // pt(3b):ext:ack:sessionid(11b)
notimp, // reserved
as(`Status:' (Failure detected!)< oil:F gas:F ice:F spot:F`),
}
var tail EHeader = 0x15D7 // use same ending for all "packets"
for i := 0; i < 8; i++ {
v := tail | (EHeader(i) << 13)
fmt.Printf("t%d :: %s\n", i, phPPs[v>>13](v))
}
Output: t0 :: Intaps: REP.gre.SAB.umg.DAG.ERR ml:W t1 :: CRCspe: v2 (Error detected!) t2 :: Unknown packet type! (2) t3 :: Unknown packet type! (3) t4 :: LinkUP: for 5591 seconds t5 :: seen: PT:5 EXT.ack Id:0x5D7 t6 :: Unknown packet type! (6) t7 :: Status: oil:2 gas:7 ice:2 spot:7
Example (Indicators) ¶
Bitpeek lets you show a bit's state in six ways:
var pics = []struct {
d string
s string
}{
{`___input`, ` B B B B`}, // B digit
{`BulbsInd`, `' @= @= @= @=`}, // @ bulb indicator
{`BitDigit`, `'t? r? a? e?`}, // labeled version of B
{`caMElize`, `'TX= RX= AK= ER=`}, // zero.ONE
{`ShowOnes`, `'TX> RX> AK> ER>`}, // only if 1
{`ShowZero`, `'TX< RX< AK< ER<\n`}, // only if 0
}
for _, n := range []uint64{11, 10, 5} {
for _, v := range pics {
fmt.Printf("%s: %s\n", v.d, Snap(v.s, n))
}
}
Output: ___input: 1 0 1 1 BulbsInd: @ ` @ @ BitDigit: t1 r0 a1 e1 caMElize: TX rx AK ER ShowOnes: TX AK ER ShowZero: RX ___input: 1 0 1 0 BulbsInd: @ ` @ ` BitDigit: t1 r0 a1 e0 caMElize: TX rx AK er ShowOnes: TX AK ShowZero: RX ER ___input: 0 1 0 1 BulbsInd: ` @ ` @ BitDigit: t0 r1 a0 e1 caMElize: tx RX ak ER ShowOnes: RX ER ShowZero: TX AK
Example (TypeMethods) ¶
// // Define types:
// type EHeader uint16
//
// // Add String() method for %s convenience:
// func (x EHeader) String() string { // make %s capable
// return string(bitpeek.Snap(
// `'PT:'F 'EXT=.ACK= Id:0xFHH`,
// uint64(x)))
// }
var ceh EHeader = 0xafdf
// use String() method:
fmt.Printf("_String: %s\n", ceh) // PT:5 ext.ACK Id:0x7DF
// // Add Verbose() method for other form of output:
// func (x EHeader) Verbose() string {
// return string(bitpeek.Snap(
// `Packet of F Type: 'Base Form,< Already ACKed,> 'Session ID: '0xFHH`,
// uint64(x)))
// }
fmt.Printf("Verbose: %s\n", ceh.Verbose())
// // add a fancy STDERR debug helper:
// func (x EHeader) D(m string) { // make debug helper
// _, _, ln, _ := runtime.Caller(1) // ln that called D
// println(ln, "DBG >>", m, ">>", x.String())
// }
//
// // use D to print on STDERR:
// ceh.D("Wasup!")
// // 172 DBG >> Wasup! >> PT:5 ext.ACK Id:0x7DF
Output: _String: PT:5 ext.ACK Id:0x7DF Verbose: Packet of 5 Type: Base Form, Already ACKed, Session ID: 0x7DF
Types ¶
This section is empty.