bitpeek

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2018 License: MIT Imports: 0 Imported by: 0

README

Bitpeek. Human readable bits.

import "github.com/ohir/bitpeek"

Overview

Bitpacked data pretty-formatter.

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.

Package has no dependencies and is faster than fmt.Sprintf used for identical output.

Taste it:

var header uint64 = 0xAfdfDeadBeef4d0e

println(string(bitpeek.Snap(
  `'Type:'F 'EXT=.ACK= Id:0xFHH from IPv4.Address32@:D.16@\n`,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)

Easy Format String

    (excerpt)	
? - show bitlabel with digit 0 or 1 in place of ?
> - show bitlabel - only if bit is SET
< - show bitlabel - only if bit is UNSET
= - show bitlabel in lowercase if bit is UNSET  
D - Decimal number
H - Hex digit: 0..F
C - Character (utf8)
I - IPv4 address

Documentation

Documentation is hosted at GoDoc project.

Linter docs too.

Install

Install package:

go get -u github.com/ohir/bitpeek

Install linter:

go get -u github.com/ohir/bplint

Revisions

  • v1.0.1 - test file annotated for linter, minor cleanups
  • v1.0.0 - first public release
  • Travis and coverage badges.

License

MIT. See LICENSE file.

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(pic string, from uint64) []byte

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL