github.com/metux/go-metabuild@v0.0.0-20240118143255-d9ed5ab697f9/util/strs/xxd.go (about)

     1  package strs
     2  
     3  import (
     4  	"bufio"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"strconv"
     9  )
    10  
    11  const (
    12  	ldigits = "0123456789abcdef"
    13  	udigits = "0123456789ABCDEF"
    14  )
    15  
    16  var (
    17  	space        = []byte(" ")
    18  	doubleSpace  = []byte("  ")
    19  	dot          = []byte(".")
    20  	newLine      = []byte("\n")
    21  	unsignedChar = []byte("unsigned char ")
    22  	unsignedInt  = []byte("};\nunsigned int ")
    23  	lenEquals    = []byte("_len = ")
    24  	brackets     = []byte("[] = {")
    25  	asterisk     = []byte("*")
    26  	commaSpace   = []byte(", ")
    27  	comma        = []byte(",")
    28  	semiColonNl  = []byte(";\n")
    29  	bar          = []byte("|")
    30  )
    31  
    32  func cfmtEncode(dst, src []byte, hextable string) {
    33  	b := src[0]
    34  	dst[3] = hextable[b&0x0f]
    35  	dst[2] = hextable[b>>4]
    36  	dst[1] = 'x'
    37  	dst[0] = '0'
    38  }
    39  
    40  func xxd(r io.Reader, w io.Writer, fname string) error {
    41  	var (
    42  		cols      int
    43  		octs      int
    44  		caps      = ldigits
    45  		doCHeader = true
    46  		doCEnd    bool
    47  		// enough room for "unsigned char NAME_FORMAT[] = {"
    48  		varDeclChar = make([]byte, 14+len(fname)+6)
    49  		// enough room for "unsigned int NAME_FORMAT = "
    50  		varDeclInt = make([]byte, 16+len(fname)+7)
    51  	)
    52  
    53  	// Generate the first and last line in the -i output:
    54  	// e.g. unsigned char foo_txt[] = { and unsigned int foo_txt_len =
    55  	// copy over "unnsigned char " and "unsigned int"
    56  	_ = copy(varDeclChar[0:14], unsignedChar[:])
    57  	_ = copy(varDeclInt[0:16], unsignedInt[:])
    58  
    59  	for i := 0; i < len(fname); i++ {
    60  		if fname[i] != '.' {
    61  			varDeclChar[14+i] = fname[i]
    62  			varDeclInt[16+i] = fname[i]
    63  		} else {
    64  			varDeclChar[14+i] = '_'
    65  			varDeclInt[16+i] = '_'
    66  		}
    67  	}
    68  	// copy over "[] = {" and "_len = "
    69  	_ = copy(varDeclChar[14+len(fname):], brackets[:])
    70  	_ = copy(varDeclInt[16+len(fname):], lenEquals[:])
    71  
    72  	cols = 12
    73  	octs = 4
    74  
    75  	// These are bumped down from the beginning of the function in order to
    76  	// allow for their sizes to be allocated based on the user's speficiations
    77  	var (
    78  		line = make([]byte, cols)
    79  		char = make([]byte, octs)
    80  	)
    81  
    82  	c := int64(0) // number of characters
    83  	r = bufio.NewReader(r)
    84  
    85  	for {
    86  		n, err := io.ReadFull(r, line)
    87  		if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
    88  			return err
    89  		}
    90  
    91  		if n == 0 {
    92  			doCEnd = true
    93  		}
    94  
    95  		if doCHeader {
    96  			w.Write(varDeclChar)
    97  			w.Write(newLine)
    98  			doCHeader = false
    99  		}
   100  
   101  		// C values
   102  		if !doCEnd {
   103  			w.Write(doubleSpace)
   104  		}
   105  		for i := 0; i < n; i++ {
   106  			cfmtEncode(char, line[i:i+1], caps)
   107  			w.Write(char)
   108  			c++
   109  			// don't add spaces to EOL
   110  			if i != n-1 {
   111  				w.Write(commaSpace)
   112  			} else if n == cols {
   113  				w.Write(comma)
   114  			}
   115  		}
   116  
   117  		if doCEnd {
   118  			w.Write(varDeclInt)
   119  			w.Write([]byte(strconv.FormatInt(c, 10)))
   120  			w.Write(semiColonNl)
   121  			return nil
   122  		}
   123  
   124  		w.Write(newLine)
   125  	}
   126  	return nil
   127  }
   128  
   129  func XXD(in string, out string) error {
   130  	inFile, err := os.Open(in)
   131  	if err != nil {
   132  		return err
   133  	}
   134  	defer inFile.Close()
   135  
   136  	outFile, err := os.Create(out)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	defer outFile.Close()
   141  
   142  	outwr := bufio.NewWriter(outFile)
   143  	defer outwr.Flush()
   144  
   145  	return xxd(inFile, outwr, filepath.Base(in))
   146  }