github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/golang/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) writeSizeInfo(size int) {
   110  	w.printf("// Size: %d bytes\n", size)
   111  }
   112  
   113  // WriteConst writes a constant of the given name and value.
   114  func (w *CodeWriter) WriteConst(name string, x interface{}) {
   115  	w.insertSep()
   116  	v := reflect.ValueOf(x)
   117  
   118  	switch v.Type().Kind() {
   119  	case reflect.String:
   120  		// See yougam/libraries/issue/13145.
   121  		const arbitraryCutoff = 16
   122  		if v.Len() > arbitraryCutoff {
   123  			w.printf("var %s %s = ", name, typeName(x))
   124  		} else {
   125  			w.printf("const %s %s = ", name, typeName(x))
   126  		}
   127  		w.WriteString(v.String())
   128  		w.printf("\n")
   129  	default:
   130  		w.printf("const %s = %#v\n", name, x)
   131  	}
   132  }
   133  
   134  // WriteVar writes a variable of the given name and value.
   135  func (w *CodeWriter) WriteVar(name string, x interface{}) {
   136  	w.insertSep()
   137  	v := reflect.ValueOf(x)
   138  	oldSize := w.Size
   139  	sz := int(v.Type().Size())
   140  	w.Size += sz
   141  
   142  	switch v.Type().Kind() {
   143  	case reflect.String:
   144  		w.printf("var %s %s = ", name, typeName(x))
   145  		w.WriteString(v.String())
   146  	case reflect.Struct:
   147  		w.gob.Encode(x)
   148  		fallthrough
   149  	case reflect.Slice, reflect.Array:
   150  		w.printf("var %s = ", name)
   151  		w.writeValue(v)
   152  		w.writeSizeInfo(w.Size - oldSize)
   153  	default:
   154  		w.printf("var %s %s = ", name, typeName(x))
   155  		w.gob.Encode(x)
   156  		w.writeValue(v)
   157  		w.writeSizeInfo(w.Size - oldSize)
   158  	}
   159  	w.printf("\n")
   160  }
   161  
   162  func (w *CodeWriter) writeValue(v reflect.Value) {
   163  	x := v.Interface()
   164  	switch v.Kind() {
   165  	case reflect.String:
   166  		w.WriteString(v.String())
   167  	case reflect.Array:
   168  		// Don't double count: callers of WriteArray count on the size being
   169  		// added, so we need to discount it here.
   170  		w.Size -= int(v.Type().Size())
   171  		w.writeSlice(x, true)
   172  	case reflect.Slice:
   173  		w.writeSlice(x, false)
   174  	case reflect.Struct:
   175  		w.printf("%s{\n", typeName(v.Interface()))
   176  		t := v.Type()
   177  		for i := 0; i < v.NumField(); i++ {
   178  			w.printf("%s: ", t.Field(i).Name)
   179  			w.writeValue(v.Field(i))
   180  			w.printf(",\n")
   181  		}
   182  		w.printf("}")
   183  	default:
   184  		w.printf("%#v", x)
   185  	}
   186  }
   187  
   188  // WriteString writes a string literal.
   189  func (w *CodeWriter) WriteString(s string) {
   190  	io.WriteString(w.Hash, s) // content hash
   191  	w.Size += len(s)
   192  
   193  	const maxInline = 40
   194  	if len(s) <= maxInline {
   195  		w.printf("%q", s)
   196  		return
   197  	}
   198  
   199  	// We will render the string as a multi-line string.
   200  	const maxWidth = 80 - 4 - len(`"`) - len(`" +`)
   201  
   202  	// When starting on its own line, go fmt indents line 2+ an extra level.
   203  	n, max := maxWidth, maxWidth-4
   204  
   205  	// Print "" +\n, if a string does not start on its own line.
   206  	b := w.buf.Bytes()
   207  	if p := len(bytes.TrimRight(b, " \t")); p > 0 && b[p-1] != '\n' {
   208  		w.printf("\"\" + // Size: %d bytes\n", len(s))
   209  		n, max = maxWidth, maxWidth
   210  	}
   211  
   212  	w.printf(`"`)
   213  
   214  	for sz, p := 0, 0; p < len(s); {
   215  		var r rune
   216  		r, sz = utf8.DecodeRuneInString(s[p:])
   217  		out := s[p : p+sz]
   218  		chars := 1
   219  		if !unicode.IsPrint(r) || r == utf8.RuneError {
   220  			switch sz {
   221  			case 1:
   222  				out = fmt.Sprintf("\\x%02x", s[p])
   223  			case 2, 3:
   224  				out = fmt.Sprintf("\\u%04x", r)
   225  			case 4:
   226  				out = fmt.Sprintf("\\U%08x", r)
   227  			}
   228  			chars = len(out)
   229  		}
   230  		if n -= chars; n < 0 {
   231  			w.printf("\" +\n\"")
   232  			n = max - len(out)
   233  		}
   234  		w.printf("%s", out)
   235  		p += sz
   236  	}
   237  	w.printf(`"`)
   238  }
   239  
   240  // WriteSlice writes a slice value.
   241  func (w *CodeWriter) WriteSlice(x interface{}) {
   242  	w.writeSlice(x, false)
   243  }
   244  
   245  // WriteArray writes an array value.
   246  func (w *CodeWriter) WriteArray(x interface{}) {
   247  	w.writeSlice(x, true)
   248  }
   249  
   250  func (w *CodeWriter) writeSlice(x interface{}, isArray bool) {
   251  	v := reflect.ValueOf(x)
   252  	w.gob.Encode(v.Len())
   253  	w.Size += v.Len() * int(v.Type().Elem().Size())
   254  	name := typeName(x)
   255  	if isArray {
   256  		name = fmt.Sprintf("[%d]%s", v.Len(), name[strings.Index(name, "]")+1:])
   257  	}
   258  	if isArray {
   259  		w.printf("%s{\n", name)
   260  	} else {
   261  		w.printf("%s{ // %d elements\n", name, v.Len())
   262  	}
   263  
   264  	switch kind := v.Type().Elem().Kind(); kind {
   265  	case reflect.String:
   266  		for _, s := range x.([]string) {
   267  			w.WriteString(s)
   268  			w.printf(",\n")
   269  		}
   270  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   271  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   272  		// nLine and nBlock are the number of elements per line and block.
   273  		nLine, nBlock, format := 8, 64, "%d,"
   274  		switch kind {
   275  		case reflect.Uint8:
   276  			format = "%#02x,"
   277  		case reflect.Uint16:
   278  			format = "%#04x,"
   279  		case reflect.Uint32:
   280  			nLine, nBlock, format = 4, 32, "%#08x,"
   281  		case reflect.Uint, reflect.Uint64:
   282  			nLine, nBlock, format = 4, 32, "%#016x,"
   283  		case reflect.Int8:
   284  			nLine = 16
   285  		}
   286  		n := nLine
   287  		for i := 0; i < v.Len(); i++ {
   288  			if i%nBlock == 0 && v.Len() > nBlock {
   289  				w.printf("// Entry %X - %X\n", i, i+nBlock-1)
   290  			}
   291  			x := v.Index(i).Interface()
   292  			w.gob.Encode(x)
   293  			w.printf(format, x)
   294  			if n--; n == 0 {
   295  				n = nLine
   296  				w.printf("\n")
   297  			}
   298  		}
   299  		w.printf("\n")
   300  	case reflect.Struct:
   301  		zero := reflect.Zero(v.Type().Elem()).Interface()
   302  		for i := 0; i < v.Len(); i++ {
   303  			x := v.Index(i).Interface()
   304  			w.gob.EncodeValue(v)
   305  			if !reflect.DeepEqual(zero, x) {
   306  				line := fmt.Sprintf("%#v,\n", x)
   307  				line = line[strings.IndexByte(line, '{'):]
   308  				w.printf("%d: ", i)
   309  				w.printf(line)
   310  			}
   311  		}
   312  	case reflect.Array:
   313  		for i := 0; i < v.Len(); i++ {
   314  			w.printf("%d: %#v,\n", i, v.Index(i).Interface())
   315  		}
   316  	default:
   317  		panic("gen: slice elem type not supported")
   318  	}
   319  	w.printf("}")
   320  }
   321  
   322  // WriteType writes a definition of the type of the given value and returns the
   323  // type name.
   324  func (w *CodeWriter) WriteType(x interface{}) string {
   325  	t := reflect.TypeOf(x)
   326  	w.printf("type %s struct {\n", t.Name())
   327  	for i := 0; i < t.NumField(); i++ {
   328  		w.printf("\t%s %s\n", t.Field(i).Name, t.Field(i).Type)
   329  	}
   330  	w.printf("}\n")
   331  	return t.Name()
   332  }
   333  
   334  // typeName returns the name of the go type of x.
   335  func typeName(x interface{}) string {
   336  	t := reflect.ValueOf(x).Type()
   337  	return strings.Replace(fmt.Sprint(t), "main.", "", 1)
   338  }