github.com/tunabay/go-bitarray@v1.3.1/bitarray_format.go (about)

     1  // Copyright (c) 2021 Hirotsuna Mizuno. All rights reserved.
     2  // Use of this source code is governed by the MIT license that can be found in
     3  // the LICENSE file.
     4  
     5  package bitarray
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  )
    12  
    13  // Format implements the fmt.Formatter interface to format BitArray values using
    14  // the standard fmt.Printf family functions.
    15  //
    16  // Verbs:
    17  // 	%b, %s  binary, 1 digit represents 1 bit.
    18  // 	%q      quoted version of %b.
    19  // 	%v      default format, same as %b.
    20  // 	%o      octal, 1 digit represents 3 bits.
    21  // 	%x, %X  hexadecimal, 1 digit represents 4 bits.
    22  //
    23  // Flags:
    24  // 	' '     (space) print a separator space every 8 digits.
    25  // 	#       more separators, ' ' and/or '-' every 4 digits.
    26  // 	-       left-justify
    27  // 	+       print the number of padding bits at the end, for %o, %x, %X
    28  func (ba BitArray) Format(s fmt.State, verb rune) {
    29  	switch verb {
    30  	case 'b', 's', 'q':
    31  		wid, widOK := s.Width()
    32  		if !widOK {
    33  			wid = 0
    34  		}
    35  		if s.Flag(int('0')) {
    36  			fmt.Fprintf(s, "%%!%c(ERROR: unsupported flag 0)", verb)
    37  			break
    38  		}
    39  		if err := ba.writeBinStr(
    40  			s, wid, s.Flag(int(' ')), s.Flag(int('#')),
    41  			verb == 'q', s.Flag(int('-')),
    42  		); err != nil {
    43  			fmt.Fprintf(s, "%%!%c(ERROR: %v)", verb, err)
    44  			break
    45  		}
    46  
    47  	case 'v':
    48  		if s.Flag(int('#')) {
    49  			fmt.Fprintf(s, "%%!%c(ERROR: unsupported flag #)", verb)
    50  			break
    51  		}
    52  		if err := ba.writeBinStr(s, 0, false, false, false, false); err != nil {
    53  			fmt.Fprintf(s, "%%!%c(ERROR: %v)", verb, err)
    54  			break
    55  		}
    56  
    57  	case 'o':
    58  		wid, widOK := s.Width()
    59  		if !widOK {
    60  			wid = 0
    61  		}
    62  		if s.Flag(int('0')) {
    63  			fmt.Fprintf(s, "%%!%c(ERROR: unsupported flag 0)", verb)
    64  			break
    65  		}
    66  		if err := ba.writeOctStr(
    67  			s, wid,
    68  			s.Flag(int(' ')), s.Flag(int('#')),
    69  			s.Flag(int('+')), s.Flag(int('-')),
    70  		); err != nil {
    71  			fmt.Fprintf(s, "%%!%c(ERROR: %v)", verb, err)
    72  			break
    73  		}
    74  
    75  	case 'x', 'X':
    76  		wid, widOK := s.Width()
    77  		if !widOK {
    78  			wid = 0
    79  		}
    80  		if s.Flag(int('0')) {
    81  			fmt.Fprintf(s, "%%!%c(ERROR: unsupported flag 0)", verb)
    82  			break
    83  		}
    84  		if err := ba.writeHexStr(
    85  			s, wid, verb == 'X',
    86  			s.Flag(int(' ')), s.Flag(int('#')),
    87  			s.Flag(int('+')), s.Flag(int('-')),
    88  		); err != nil {
    89  			fmt.Fprintf(s, "%%!%c(ERROR: %v)", verb, err)
    90  			break
    91  		}
    92  
    93  	default:
    94  		fmt.Fprintf(s, "%%!%c(BitArray=", verb)
    95  		if err := ba.writeBinStr(s, 0, true, true, true, false); err != nil {
    96  			fmt.Fprintf(s, "(ERROR: %v)", err)
    97  		}
    98  		fmt.Fprint(s, ")")
    99  	}
   100  }
   101  
   102  func (ba *BitArray) writeBinStr(wr io.Writer, wid int, sep8, sep4, quote, left bool) error {
   103  	sep8 = sep8 || sep4
   104  	sLen := ba.Len()
   105  	if 0 < ba.nBits && sep8 {
   106  		n4d := (ba.nBits + 3) >> 2 // num of 1111
   107  		n8d := (n4d + 1) >> 1      // num of 11111111
   108  		if sep4 {
   109  			sLen += n4d - 1
   110  		} else {
   111  			sLen += n8d - 1
   112  		}
   113  	}
   114  	if quote {
   115  		sLen += 2
   116  	}
   117  	if wid < sLen {
   118  		wid = sLen
   119  	}
   120  	if !left {
   121  		if pad := wid - sLen; 0 < pad {
   122  			if _, err := wr.Write(bytes.Repeat([]byte{' '}, pad)); err != nil {
   123  				return fmt.Errorf("pad-l: %w", err)
   124  			}
   125  		}
   126  	}
   127  	if quote {
   128  		if _, err := wr.Write([]byte{'"'}); err != nil {
   129  			return fmt.Errorf("quote-l: %w", err)
   130  		}
   131  	}
   132  	octbuf := make([]byte, 9)
   133  	for i := 0; i < (ba.nBits+7)>>3; i++ {
   134  		var wb byte
   135  		if ba.b != nil {
   136  			wb = ba.b[i]
   137  		}
   138  		if sep8 && i != 0 {
   139  			if _, err := wr.Write([]byte{' '}); err != nil {
   140  				return fmt.Errorf("oct-sep: %w", err)
   141  			}
   142  		}
   143  		cLSB := 0                   // num of LSBs to be cleared
   144  		if i+1 == (ba.nBits+7)>>3 { // last byte
   145  			cLSB = ba.NumPadding()
   146  		}
   147  		bi := 0
   148  		for j := 7; cLSB <= j; j-- {
   149  			if sep4 && j == 3 {
   150  				octbuf[bi] = byte('-')
   151  				bi++
   152  			}
   153  			octbuf[bi] = '0' + wb>>j&1
   154  			bi++
   155  		}
   156  		if _, err := wr.Write(octbuf[:bi]); err != nil {
   157  			return fmt.Errorf("byte: %d(%d): %w", i, cLSB, err)
   158  		}
   159  	}
   160  	if quote {
   161  		if _, err := wr.Write([]byte{'"'}); err != nil {
   162  			return fmt.Errorf("quote-r: %w", err)
   163  		}
   164  	}
   165  	if left {
   166  		if pad := wid - sLen; 0 < pad {
   167  			if _, err := wr.Write(bytes.Repeat([]byte{' '}, pad)); err != nil {
   168  				return fmt.Errorf("pad-r: %w", err)
   169  			}
   170  		}
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  func (ba *BitArray) writeOctStr(wr io.Writer, wid int, sep8, sep4, rpad, left bool) error {
   177  	sep8 = sep8 || sep4
   178  	ntri, nfrc := ba.nBits/3, ba.nBits%3 // num of 777, and remaining bits
   179  	if nfrc != 0 {
   180  		ntri++
   181  	}
   182  	npad := (3 - nfrc) % 3 // num of last padding bits
   183  
   184  	sLen := ntri
   185  	if 0 < ba.nBits && sep8 {
   186  		n4d := (ntri + 3) >> 2 // num of 7777
   187  		n8d := (n4d + 1) >> 1  // num of 77777777
   188  		if sep4 {
   189  			sLen += n4d - 1
   190  		} else {
   191  			sLen += n8d - 1
   192  		}
   193  	}
   194  	if rpad && npad != 0 { // (pad=2)
   195  		if sep8 {
   196  			sLen++
   197  		}
   198  		sLen += 7
   199  	}
   200  	if wid < sLen {
   201  		wid = sLen
   202  	}
   203  	if !left {
   204  		if pad := wid - sLen; 0 < pad {
   205  			if _, err := wr.Write(bytes.Repeat([]byte{' '}, pad)); err != nil {
   206  				return fmt.Errorf("pad-l: %w", err)
   207  			}
   208  		}
   209  	}
   210  
   211  	iby, ibi := 0, 0 // source cursor on ba.b, byte and bit
   212  	for i := 0; i < ntri; i++ {
   213  		if sep8 && i != 0 && i&3 == 0 {
   214  			if i&7 == 0 {
   215  				if _, err := wr.Write([]byte{' '}); err != nil {
   216  					return fmt.Errorf("sep8: %w", err)
   217  				}
   218  			} else if sep4 {
   219  				if _, err := wr.Write([]byte{'-'}); err != nil {
   220  					return fmt.Errorf("sep4: %w", err)
   221  				}
   222  			}
   223  		}
   224  		var b byte
   225  		if ba.b != nil {
   226  			b = ba.b[iby]
   227  		}
   228  		rsf := (5 - ibi)
   229  		if 0 < rsf {
   230  			b >>= rsf
   231  		} else {
   232  			b <<= -rsf
   233  		}
   234  		b &= 7
   235  		ibi += 3
   236  		if 7 < ibi {
   237  			iby++
   238  			if iby < (ba.nBits+7)>>3 {
   239  				ibi &= 7
   240  				if ba.b != nil {
   241  					b |= ba.b[iby] >> (8 - ibi) & byte(ibi|1)
   242  				}
   243  			}
   244  		}
   245  		if _, err := wr.Write([]byte{'0' + b}); err != nil {
   246  			return fmt.Errorf("oct: %w", err)
   247  		}
   248  	}
   249  	if rpad && npad != 0 { // (pad=2)
   250  		b := []byte{' ', '(', 'p', 'a', 'd', '=', '0' + byte(npad), ')'}
   251  		if !sep8 {
   252  			b = b[1:]
   253  		}
   254  		if _, err := wr.Write(b); err != nil {
   255  			return fmt.Errorf("npad: %w", err)
   256  		}
   257  	}
   258  	if left {
   259  		if pad := wid - sLen; 0 < pad {
   260  			if _, err := wr.Write(bytes.Repeat([]byte{' '}, pad)); err != nil {
   261  				return fmt.Errorf("pad-r: %w", err)
   262  			}
   263  		}
   264  	}
   265  
   266  	return nil
   267  }
   268  
   269  func (ba *BitArray) writeHexStr(wr io.Writer, wid int, upper, sep8, sep4, rpad, left bool) error {
   270  	const (
   271  		hexCharsL = "0123456789abcdef"
   272  		hexCharsU = "0123456789ABCDEF"
   273  	)
   274  	sep8 = sep8 || sep4
   275  	nnbl := (ba.nBits + 3) >> 2 // num of f
   276  	npad := ba.NumPadding() & 3
   277  	sLen := nnbl
   278  	if 0 < ba.nBits && sep8 {
   279  		n4d := (nnbl + 3) >> 2 // num of ffff
   280  		n8d := (n4d + 1) >> 1  // num of ffffffff
   281  		if sep4 {
   282  			sLen += n4d - 1
   283  		} else {
   284  			sLen += n8d - 1
   285  		}
   286  	}
   287  	if rpad && npad != 0 { // (pad=3)
   288  		if sep8 {
   289  			sLen++
   290  		}
   291  		sLen += 7
   292  	}
   293  	if wid < sLen {
   294  		wid = sLen
   295  	}
   296  	if !left {
   297  		if pad := wid - sLen; 0 < pad {
   298  			if _, err := wr.Write(bytes.Repeat([]byte{' '}, pad)); err != nil {
   299  				return fmt.Errorf("pad-l: %w", err)
   300  			}
   301  		}
   302  	}
   303  	hexc := hexCharsL
   304  	if upper {
   305  		hexc = hexCharsU
   306  	}
   307  	for i := 0; i < nnbl; i++ {
   308  		if i != 0 && sep8 && i&3 == 0 {
   309  			if sep4 || i&7 == 0 {
   310  				if _, err := wr.Write([]byte{' '}); err != nil {
   311  					return fmt.Errorf("sep: %w", err)
   312  				}
   313  			}
   314  		}
   315  		boff := 4 ^ i&1<<2
   316  		var b byte
   317  		if ba.b != nil {
   318  			b = ba.b[i>>1]
   319  		}
   320  		if _, err := wr.Write([]byte{hexc[b>>boff&0xf]}); err != nil {
   321  			return fmt.Errorf("nibble: %w", err)
   322  		}
   323  	}
   324  	if rpad && npad != 0 { // (pad=3)
   325  		b := []byte{' ', '(', 'p', 'a', 'd', '=', '0' + byte(npad), ')'}
   326  		if !sep8 {
   327  			b = b[1:]
   328  		}
   329  		if _, err := wr.Write(b); err != nil {
   330  			return fmt.Errorf("npad: %w", err)
   331  		}
   332  	}
   333  	if left {
   334  		if pad := wid - sLen; 0 < pad {
   335  			if _, err := wr.Write(bytes.Repeat([]byte{' '}, pad)); err != nil {
   336  				return fmt.Errorf("pad-r: %w", err)
   337  			}
   338  		}
   339  	}
   340  
   341  	return nil
   342  }