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 }