github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/text/internal/gen/code.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gen
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/gob"
    10  	"fmt"
    11  	"hash"
    12  	"hash/fnv"
    13  	"io"
    14  	"log"
    15  	"os"
    16  	"reflect"
    17  	"strings"
    18  	"unicode"
    19  	"unicode/utf8"
    20  )
    21  
    22  // This file contains utilities for generating code.
    23  
    24  // TODO: other write methods like:
    25  // - slices, maps, types, etc.
    26  
    27  // CodeWriter is a utility for writing structured code. It computes the content
    28  // hash and size of written content. It ensures there are newlines between
    29  // written code blocks.
    30  type CodeWriter struct {
    31  	buf  bytes.Buffer
    32  	Size int
    33  	Hash hash.Hash32 // content hash
    34  	gob  *gob.Encoder
    35  	// For comments we skip the usual one-line separator if they are followed by
    36  	// a code block.
    37  	skipSep bool
    38  }
    39  
    40  func (w *CodeWriter) Write(p []byte) (n int, err error) {
    41  	return w.buf.Write(p)
    42  }
    43  
    44  // NewCodeWriter returns a new CodeWriter.
    45  func NewCodeWriter() *CodeWriter {
    46  	h := fnv.New32()
    47  	return &CodeWriter{Hash: h, gob: gob.NewEncoder(h)}
    48  }
    49  
    50  // WriteGoFile appends the buffer with the total size of all created structures
    51  // and writes it as a Go file to the the given file with the given package name.
    52  func (w *CodeWriter) WriteGoFile(filename, pkg string) {
    53  	f, err := os.Create(filename)
    54  	if err != nil {
    55  		log.Fatalf("Could not create file %s: %v", filename, err)
    56  	}
    57  	defer f.Close()
    58  	if _, err = w.WriteGo(f, pkg); err != nil {
    59  		log.Fatalf("Error writing file %s: %v", filename, err)
    60  	}
    61  }
    62  
    63  // WriteGo appends the buffer with the total size of all created structures and
    64  // writes it as a Go file to the the given writer with the given package name.
    65  func (w *CodeWriter) WriteGo(out io.Writer, pkg string) (n int, err error) {
    66  	sz := w.Size
    67  	w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32())
    68  	defer w.buf.Reset()
    69  	return WriteGo(out, pkg, w.buf.Bytes())
    70  }
    71  
    72  func (w *CodeWriter) printf(f string, x ...interface{}) {
    73  	fmt.Fprintf(w, f, x...)
    74  }
    75  
    76  func (w *CodeWriter) insertSep() {
    77  	if w.skipSep {
    78  		w.skipSep = false
    79  		return
    80  	}
    81  	// Use at least two newlines to ensure a blank space between the previous
    82  	// block. WriteGoFile will remove extraneous newlines.
    83  	w.printf("\n\n")
    84  }
    85  
    86  // WriteComment writes a comment block. All line starts are prefixed with "//".
    87  // Initial empty lines are gobbled. The indentation for the first line is
    88  // stripped from consecutive lines.
    89  func (w *CodeWriter) WriteComment(comment string, args ...interface{}) {
    90  	s := fmt.Sprintf(comment, args...)
    91  	s = strings.Trim(s, "\n")
    92  
    93  	// Use at least two newlines to ensure a blank space between the previous
    94  	// block. WriteGoFile will remove extraneous newlines.
    95  	w.printf("\n\n// ")
    96  	w.skipSep = true
    97  
    98  	// strip first indent level.
    99  	sep := "\n"
   100  	for ; len(s) > 0 && (s[0] == '\t' || s[0] == ' '); s = s[1:] {
   101  		sep += s[:1]
   102  	}
   103  
   104  	strings.NewReplacer(sep, "\n// ", "\n", "\n// ").WriteString(w, s)
   105  
   106  	w.printf("\n")
   107  }
   108  
   109  func (w *CodeWriter) writeSizeAndElementInfo(size, n int) {
   110  	w.printf("// Size: %d bytes, %d elements\n", size, n)
   111  }
   112  
   113  func (w *CodeWriter) writeSizeInfo(size int) {
   114  	w.printf("// Size: %d bytes\n", size)
   115  }
   116  
   117  // WriteConst writes a constant of the given name and value.
   118  func (w *CodeWriter) WriteConst(name string, x interface{}) {
   119  	w.insertSep()
   120  	v := reflect.ValueOf(x)
   121  
   122  	switch v.Type().Kind() {
   123  	case reflect.String:
   124  		w.writeSizeInfo(v.Len())
   125  		// See golang.org/issue/13145.
   126  		const arbitraryCutoff = 16
   127  		if v.Len() > arbitraryCutoff {
   128  			w.printf("var %s %s = ", name, typeName(x))
   129  		} else {
   130  			w.printf("const %s %s = ", name, typeName(x))
   131  		}
   132  		w.WriteString(v.String())
   133  		w.printf("\n")
   134  	default:
   135  		w.printf("const %s = %#v\n", name, x)
   136  	}
   137  }
   138  
   139  // WriteVar writes a variable of the given name and value.
   140  func (w *CodeWriter) WriteVar(name string, x interface{}) {
   141  	w.insertSep()
   142  	v := reflect.ValueOf(x)
   143  	sz := int(v.Type().Size())
   144  
   145  	switch v.Type().Kind() {
   146  	case reflect.String:
   147  		w.writeSizeInfo(v.Len() + sz)
   148  		w.Size += sz
   149  		w.printf("var %s %s = ", name, typeName(x))
   150  		w.WriteString(v.String())
   151  	case reflect.Slice:
   152  		w.writeSizeAndElementInfo(sizeOfArray(x)+sz, v.Len())
   153  		w.Size += sz
   154  		w.printf("var %s = ", name)
   155  		w.writeSlice(x, false, true)
   156  	case reflect.Array:
   157  		w.writeSizeAndElementInfo(sz, v.Len())
   158  		w.printf("var %s = ", name)
   159  		w.writeSlice(x, true, true)
   160  	default:
   161  		w.printf("var %s %s = ", name, typeName(x))
   162  		w.Size += sz
   163  		// TODO: size info?
   164  		w.gob.Encode(x)
   165  		w.printf("%#v", x)
   166  	}
   167  	w.printf("\n")
   168  }
   169  
   170  // WriteString writes a string literal.
   171  func (w *CodeWriter) WriteString(s string) {
   172  	io.WriteString(w.Hash, s) // content hash
   173  	w.Size += len(s)
   174  
   175  	const maxInline = 40
   176  	if len(s) <= maxInline {
   177  		w.printf("%q", s)
   178  		return
   179  	}
   180  
   181  	// We will render the string as a multi-line string.
   182  	const maxWidth = 80 - 4 - len(`"`) - len(`" +`)
   183  
   184  	// When starting on its own line, go fmt indents line 2+ an extra level.
   185  	n, max := maxWidth, maxWidth-4
   186  
   187  	// Print "" +\n, if a string does not start on its own line.
   188  	b := w.buf.Bytes()
   189  	if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' {
   190  		w.printf("\"\" +\n")
   191  		n, max = maxWidth, maxWidth
   192  	}
   193  
   194  	w.printf(`"`)
   195  
   196  	for sz, p := 0, 0; p < len(s); {
   197  		var r rune
   198  		r, sz = utf8.DecodeRuneInString(s[p:])
   199  		out := s[p : p+sz]
   200  		chars := 1
   201  		if !unicode.IsPrint(r) || r == utf8.RuneError {
   202  			switch sz {
   203  			case 1:
   204  				out = fmt.Sprintf("\\x%02x", s[p])
   205  			case 2, 3:
   206  				out = fmt.Sprintf("\\u%04x", r)
   207  			case 4:
   208  				out = fmt.Sprintf("\\U%08x", r)
   209  			}
   210  			chars = len(out)
   211  		}
   212  		if n -= chars; n < 0 {
   213  			w.printf("\" +\n\"")
   214  			n = max - len(out)
   215  		}
   216  		w.printf("%s", out)
   217  		p += sz
   218  	}
   219  	w.printf(`"`)
   220  }
   221  
   222  // WriteSlice writes a slice value.
   223  func (w *CodeWriter) WriteSlice(x interface{}) {
   224  	w.writeSlice(x, false, false)
   225  }
   226  
   227  // WriteArray writes an array value.
   228  func (w *CodeWriter) WriteArray(x interface{}) {
   229  	w.writeSlice(x, true, false)
   230  }
   231  
   232  func (w *CodeWriter) writeSlice(x interface{}, isArray, isVar bool) {
   233  	v := reflect.ValueOf(x)
   234  	w.gob.Encode(v.Len())
   235  	w.Size += v.Len() * int(v.Type().Elem().Size())
   236  
   237  	name := typeName(x)
   238  	if isArray {
   239  		name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:])
   240  	}
   241  	if isArray || isVar {
   242  		w.printf("%s{\n", name)
   243  	} else {
   244  		w.printf("%s{ // %d elements\n", name, v.Len())
   245  	}
   246  
   247  	switch kind := v.Type().Elem().Kind(); kind {
   248  	case reflect.String:
   249  		for _, s := range x.([]string) {
   250  			w.WriteString(s)
   251  			w.printf(",\n")
   252  		}
   253  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   254  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   255  		// nLine and nBlock are the number of elements per line and block.
   256  		nLine, nBlock, format := 8, 64, "%d,"
   257  		switch kind {
   258  		case reflect.Uint8:
   259  			format = "%#02x,"
   260  		case reflect.Uint16:
   261  			format = "%#04x,"
   262  		case reflect.Uint32:
   263  			nLine, nBlock, format = 4, 32, "%#08x,"
   264  		case reflect.Uint, reflect.Uint64:
   265  			nLine, nBlock, format = 4, 32, "%#016x,"
   266  		case reflect.Int8:
   267  			nLine = 16
   268  		}
   269  		n := nLine
   270  		for i := 0; i < v.Len(); i++ {
   271  			if i%nBlock == 0 && v.Len() > nBlock {
   272  				w.printf("// Entry %X - %X\n", i, i+nBlock-1)
   273  			}
   274  			x := v.Index(i).Interface()
   275  			w.gob.Encode(x)
   276  			w.printf(format, x)
   277  			if n--; n == 0 {
   278  				n = nLine
   279  				w.printf("\n")
   280  			}
   281  		}
   282  		w.printf("\n")
   283  	case reflect.Struct:
   284  		for i := 0; i < v.Len(); i++ {
   285  			x := v.Index(i).Interface()
   286  			w.gob.EncodeValue(v)
   287  			line := fmt.Sprintf("%#v,\n", x)
   288  			line = line[strings.IndexByte(line, '{'):]
   289  			w.printf(line)
   290  		}
   291  	default:
   292  		panic("gen: slice type not supported")
   293  	}
   294  	w.printf("}")
   295  }
   296  
   297  func sizeOfArray(x interface{}) int {
   298  	v := reflect.ValueOf(x)
   299  
   300  	size := v.Len() * int(v.Type().Elem().Size())
   301  	switch v.Type().Elem().Kind() {
   302  	case reflect.String:
   303  		for _, s := range x.([]string) {
   304  			size += len(s)
   305  		}
   306  	case reflect.Slice:
   307  		for i := 0; i < v.Len(); i++ {
   308  			size += sizeOfArray(v.Index(i).Interface())
   309  		}
   310  	case reflect.Array, reflect.Ptr, reflect.Map:
   311  		panic("gen: array element not supported.")
   312  	}
   313  	return size
   314  }
   315  
   316  // WriteType writes a definition of the type of the given value and returns the
   317  // type name.
   318  func (w *CodeWriter) WriteType(x interface{}) string {
   319  	t := reflect.TypeOf(x)
   320  	w.printf("type %s struct {\n", t.Name())
   321  	for i := 0; i < t.NumField(); i++ {
   322  		w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type)
   323  	}
   324  	w.printf("}\n")
   325  	return t.Name()
   326  }
   327  
   328  // typeName returns the name of the go type of x.
   329  func typeName(x interface{}) string {
   330  	t := reflect.ValueOf(x).Type()
   331  	return strings.Replace(fmt.Sprint(t), "main.", "", 1)
   332  }